• 关于线程的执行和退出


    线程由两部分构成:

    线程内核对象:操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。

    线程堆栈:它用于维护线程在执行代码时需要的所有函数参数和局部变量。

     

    我们必需要知道一个事实 ----- 进程( 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 退出整个进程。

  • 相关阅读:
    一二类斯特林数小结
    我的博客搬家了!
    常用编程思想与算法
    【spring专题】spring简介
    九、 Java程序初始化的顺序(二)
    八、 Java程序初始化的顺序(一)
    python的上下文管理
    七、 面向对象(二)
    六、 面向对象基础
    paramiko模块及ssh远程登陆
  • 原文地址:https://www.cnblogs.com/qilinzi/p/1940486.html
Copyright © 2020-2023  润新知