线程由两部分构成:
线程内核对象:操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。
线程堆栈:它用于维护线程在执行代码时需要的所有函数参数和局部变量。
我们必需要知道一个事实 ----- 进程( WINDOWS 中)是不能被调度的。通常所说的调度,都是对线程而言的。进程仅仅是线程的容器,是系统统分配资源的单位。 这样,线程自身不会分配资源。同一进程中的多个线程共同使用它们父进程的资源。
一个进程至少需一个线程,如果一个进程中不存在线程(所有线程都退出)。则系统认为这个进程没有存在的必要,于是便会撤消这个进程,并释放他占用的资源。
在 VC 环境中,我们可以调用 CreatThread 来创建一个线程。关于用法请查 MSDN
我们应该主要关注这个 API 的调用,操作系统会干哪些事情。而不是仅仅会使用这个 API 。
当程序启动的时候,系统会创建一个进程,进而会为这个进程创建一个主线程。主线程即是一个程序的入口。 C/C++ 运行期库会初始化全局的 C/C++ 变量,然后调用入口点函数(如 main )。然后初始化线程堆栈,创建临时变量等。
当线程结束时会按以下操作执行。
1、 在线程函数中创建的所有 C++ 对象均将通过它们的撤消函数正确地撤消。
2、 操作系统将正确地释放线程堆栈使用的内存。
3、 系统将线程退出代码设置为线程函数的返回值。
4、 系统将递减线程内核对象的引用计数。
因此,正确的线程退出方式是必不可少的。 最好的退出线程的方法是让线程函数返回,这样 C/C++ 运行期库便能正确地撤消 C/C++ 对象。 值一说的是,不管线程怎么退出,当一个程序结束时,系统总是回清除他使用了的系统资源,如内核对象等。但 C/C+= 运行期库申请的资源则不会被释放。
像 ExitThread 等函数,需要三思而用。
众所周知,创建线程的时候,必不可少的两个参数就是线程入口函数和函数参数。那这两个是怎么工作的呢。请看如下分析。
首先要知道,每个线程都有自己的一组 CPU 寄存器。这组寄存器的所有信息就是我们所说的线程上下文。当系统进行时间片轮转执行线程的时候,会将前一个线程的所有寄存器值保存,然后将下一个需要执行的线程的寄存器值装入 CPU 寄存器,进而执行。重复这样的操作,就达到了多线程执行的效果。当然,实际情况是很复杂的。需要许多的判断,在多处理器计算机中,还需要考虑线程的亲缘性等。
话不多说,回头来看这个线程函数的执行方式。
当一个线程被创建时,系统便会创建线程的内核对象。并分配线程堆栈需要的内存。这个内存是从进程的地址空间分配而来的,因为我们知道,线程不持有资源。当一切都做好后,便是填写数据了。
系统首先会把线程的参数和入口地址压入线程堆栈。 注意,是先压的参数,再压的入口地址。这样入口地址是在栈顶。此时 SP 的值即为函数入口地址。 而将 IP 设置为另一个函数。它叫 void BaseThreadStart(); 很神奇吧,为什么 IP 并不是线程的入口函数地址呢。 我们直接可以猜到, void BaseThreadStart(); 所做的工作中,会调用到我们的线程入口函数。接下来我们看看 void BaseThreadStart(); 函数干了些什么。
void BaseThreadStart(PTHREAD_START_ROUTINE pfnStartAddr,PVOID pvParam)
{
__try{
Exithread((pfnStartAddr)(pvParam));
}
__except(UnhandedExceptionFilter(GetExceptionInformation()){
ExitProcess(GetExceptionCode());
}
}
还记得我们压栈的时候是先压的参数再压的线程函数入口地址么。这就对了,上面即是这样的情况。 可以看出, BaseThreadStart 函数不会马上返回,他会等待线程结束后返回。由于线程和进程共用的进程内存空间,可以知道。如果线程函数没有退出 BaseThreadStart 就返回,可以肯定的会引发访问违规。因为线程未退出时,线程堆栈并未清除。 BaseThreadStart 返回时,从堆栈上得到的返回地址是一个不可预测的值。 进程在初始化时也会是类似执行方式。因为进程执行时,启动的是一个主线程,而不是由进程本身来执行代码。
最后说明,当新线程执行 BaseThreadStart 函数时,将会出现如下情况:
1、 在线程函数中建立一个 SHE ,这样,在线程执行时产生的任何异常情况都将会得到系统的某种默认处理。
2、 系统调用线程函数,并将你传递的参数传递给它。
3、 当线程函数返回时, BaseThreadStart 调用 Exithread ,并将线程函数的返回值传递给它。该线程内核对象的使用计数减 1 ,线程停止运行。
4、 如果线程产生一个没有处理的异常条件,由 BaseThreadStart 建立的 SHE 将负责处理该异常。由上面可以看出,它调用 ExitProcess 退出整个进程。