《Windows核心编程》学习——线程基础

线程的组成:

1.一个线程内核对象,操作系统用它来管理线程。内核对象中还存储了线程的各种统计信息,包括挂起计数、退出代码等,以便于系统对线程的管理。内核对象中有一个CONTEXT结构,这个结构中存储了线程上一次执行的时候CPU寄存器的状态。

2.一个线程栈,用于维护线程执行时所需的所有函数参量和局部变量。

 

 

线程的运行:

在解释线程的运行机制之前,首先回顾一下过去单线程程序的运行机制:

1.程序是一条指令接着一条指令顺序执行的,回忆下之前单片机课上学习的汇编语言。

2.CPU现在正在执行的指令地址被存储在IP寄存器里面,这条指令执行完之后,IP就会自动增加,这样CPU下次就会接着执行下一条指令了。这个可以通过DEBUG指令在命令行里查看一下,具体还是要回忆下之前《汇编语言》这本书里面学到的东西。

3.为了便于管理,在汇编代码里经常把程序分成几个"",包括"代码段""栈段""数据段"。这样把所有代码指令都放到同一个地方,临时变量之类的放到另外一个地方,管理起来容易不少,后来栈的设计被CPU广泛支持,有一个寄存器SP专门就用来存储栈顶指针。具体的还是要回忆下《汇编语言》里学的东西。

 

下面解释Windows下多线程程序的运行机制:

1.如果有两个CPU的话,运行两个线程,事情当然好办许多,两个CPU各有自己的IPSP寄存器,代码段应该是共享的、只需要分别建立两个"栈段",两个CPU各自访问自己的临时数据即可,还是原来的指令顺序执行,互相并不干扰。

2.而对于一个CPU运行两个线程的情况,事情就比较麻烦,毕竟需要用一个CPU模拟出两个CPU的效果。想要达到这种效果,最直观的办法当然是有一个变量把每个线程当前执行的状况——也就是当前CPU的寄存器值存储下来。每次从当前线程切换到另一个线程的时候,都先把当前线程的寄存器状况存下来,然后把另一个线程之前已经存好的寄存器状况载入到CPU里去执行。为了两个线程之间互不干扰,它们也应该各自有一个"栈段",来存储自己执行过程中的临时变量什么的。

3.Windows系统里当前CPU寄存器的值以CONTEXT结构的形式被存储在线程内核对象里面。《Windows核心编程》一书中有CONTEXT结构定义,可以看到其中确实有ESP,EIP寄存器。

4.线程内核对象之中还记录了挂起计数、使用计数等信息。Windows用这些信息来管理线程。

5.Windows中多线程程序的运行机制简述如下:

每隔大概20msWindows都会查看所有当前存在的线程内核对象,在这些对象中,只有一部分被认为是可调度的(可根据线程内核对象中记录的信息来判断)Windows根据某种算法在可调度的线程内核对象中选一个,并将上次保存在线程上下文(CONTEXT)中的值载入CPU寄存器。这一操作被称为上下文切换。线程上下文中的值载入CPU寄存器之后,也就把上一次线程的运行状态装载到了CPU里面,线程所执行的指令也就可以被运行了。

 

现在,应该可以理解为什么线程的组成只是线程内核对象和线程栈这么简单,也理解了线程之间切换时为什么线程中的指令能够接着上次没执行完的地方继续执行,另外,还理解了通过线程内核对象,Windows才能对线程进行有效的管理——内核对象里记录了线程那么多的信息,而线程栈仅仅是为线程执行提供临时空间而已。

 

 

线程的创建:

Windows中负责线程创建的APICreateThread,虽然_beginthreadex等也可以创建线程,但只不过是调用了CreateThread而已。下面介绍CreateThread是如何创建一个线程的:

1.一个线程内核对象被创建出来,需要注意的是它的挂起计数被设定为1,这个时候线程还没创建完毕,但是线程内核对象已经创建出来了,为了使系统轮询的时候线程不被执行到,挂起计数不能被设定为0。另外这个时候内核对象的CONTEXT结构里的值还没被设定。

2.线程栈被创建出来,其中被传进了两个变量,一个是线程函数的地址,一个是线程函数的参数。这两个变量在之后函数调用的时候会用到。

3.内核对象的CONTEXT结构里,IP寄存器的值被设定为RtlUserThreadStart函数的地址,而SP寄存器的值指向了之前创建好的线程栈,通过SP就可以找到之前传到栈里的两个变量了。

4.经过以上操作,线程就被初始化好了,接着启动便是,把挂起计数递减到0。系统轮询线程内核对象的时候,发现此线程可以执行了,就执行CONTEXT寄存器里存储的指令,在这里也就是执行RtlUserThreadStart函数,RtlUserThreadStart函数有两个参数,这两个参数就是由SP来指定的那两个变量——线程函数地址和线程函数参数。

5.RtlUserThreadStart会在内部调用线程函数,并把参数传到线程函数里,这样线程函数就被执行起来了。

6.当线程函数返回的时候,RtlUserThreadStart会为它调用ExitThread来结束线程。

 

 

线程的销毁:

销毁一个线程有多种方式,方式之间各有区别。下面逐一介绍:

1.线程函数退出:

这种方式是最正常和最正确的方式。因为是从函数里正常退出的,在线程执行过程中创建的临时对象都能够调用它们的析构函数而死去。退出之后会执行ExitThread函数来销毁线程所占有的系统资源——正如之前的介绍,线程函数其实是退出到了RtlUserThreadStart函数那里,有RtlUserThreadStart函数来负责调用ExitThread

2.调用ExitThread

这种方式不大好的地方在于线程执行过程中创建的临时对象没办法调用它们自己的析构函数,根据MSDN的说法,ExitThread将会直接销毁本线程所拥有的线程栈——当然也包括线程栈中存储的临时对象。在实际情况中我们经常在构造函数里new出一块内存来使用,析构的时候再delete它,如果对这种情况调用了ExitThread,析构函数无法被调用,new出来的那块内存也就没办法释放了,造成了内存泄漏。所以不推荐这种线程退出的方式。

ExitThread还会执行一些系统上的操作,比如内核对象使用计数递减,关闭线程中使用过的任何I/O端口,与使用过的DLL脱离关系等等。

3.调用TerminateThread

TerminateThread相对与ExitThread来说更不推荐使用,除非在极其了解要终结的线程代码如何构建的情况下,TerminateThread才有可能被安全地使用,它所造成的危害在MSDN上已经有比较清楚的说明。比如堆没法释放,关键代码段没机会关闭等等。

并且TerminateThread不会销毁线程的线程栈,但他会把线程内核对象的使用计数递减——这意味着线程内核对象有可能已经被销毁了,但是线程栈还存在着。Microsoft故意以这种方式来实现TerminateThread。否则,加入其他还在运行的线程要引用被杀死的那个线程的堆栈上的值,就会引起访问违规。让被杀死的线程的堆栈保留在内存中,其他的线程就可以正常运行。

4.包含线程的进程终止时:

进程终止的时候会杀死所有的线程,不知道具体杀死的方法是什么。

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值