九、线程内幕

  CreateThread 函数的一个调用 导致 系统创建一个线程内核对象,该对象初的使用计数为2。( 创建线程内核对象加1,返回线程内核对象句柄加1 ),所以除非线程终止,而且 CreateThread 返回的句柄关闭,否则线程内核对象不会被销毁。该线程对象的其它属性也被初始化:暂停计数被设为1,退出代码被设备STILE_ACTIVE(0x103),而且对象被设为未触发状态。

  创建了内核对象,系统分配内存,供线程的堆栈使用。此内存是从进程的地址空间分配的,因为线程没有自己的地址空间。系统将来个值写入新线程堆栈的上端,如图1所示,即调用的线程函数及其参数。

  每个线程都有自己的一组CPU寄存器,称为线程的上下文(context)。上下文反映了当线程上一次执行时,线程CPU寄存器的状态。CONTEXT结构保存在线程的内核对象中。

  当线程内核对象被初始化的时候,CONTEXT结构的堆栈指针寄存器被设为pfnStartAddr在线程堆栈中的地址。而指令指针寄存器被设为RtlUserThreadStart函数的地址。


VOID  RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {
             __try {
                      ExitThread((pfnStartAddr)(pvParam));
                 }
            __except(UnhandledExceptionFilter(GetExceptionInformation())) {
                    ExitProcess(GetExceptionCode());
               }
           // NOTE: We never get here.
    }


  线程完全初始化之后,系统检查CREATE_SUSPENDED标志是否已被传给CreateThread函数。如果此标记没有传递,系统将线程的挂起计数递减至0;随后,线程可以调度给一个处理器去执行。然后,系统在实际的 CPU寄存器中加载上一次在线程上下文中保存的值。现在,线程可以在其进程的地址空间中执行代码并处理数据了。

  新线程执行RtlUserThreadStart函数的时候,将发生以下事情:

  围绕线程函数,会设置一个结构化异常处理(SEH)帧。这样一来,线程执行期间所产生的任何异常都能得到系统的默认处理。

  系统调用线程函数,把传给CreateThread函数的pvParam参数传给它。

  线程函数返回时,RtlUserThreadStart调用ExitThread,将你的线程函数的返回值传给它。线程内核对象的使用计数递减,而后线程停止执行。

  如果线程产生了一个未被处理的异常,RtlUserThreadStart函数所设置的SEH帧会处理这个异常。通常,这意味着系统会向用户显示一个消息框,而且当用户关闭此消息框时,RtlUserThreadStart会调用ExitProcess来终止真个进程,而不是终止有问题的线程。

  当一个进程的主线程初始化时,其指令指针指向RtlUserThreadStart,当RtlUserThreadStart开始执行时,它会调用C/C++运行库的启动代码,后者初始化继而调用你的_tmain或_tWinMain函数。

  十、C/C++运行库注意事项

  为了保证C和C++多线程应用程序正常运行,必须创建一个数据结构,并使之与使用了C/C++运行库函数的每个线程关联。然后,在调用C/C++运行库函数时,那些函数必须知道去查找主调线程的数据块,从而避免影响到其它线程

  编写C/C++应用程序,一定不要调用操作系统的CreateThread函数,相反,应该调用C/C++运行库函数_beginthreadex:


 uintptr_t __cdecl _beginthreadex (  void *psa, unsigned cbStackSize,  unsigned (__stdcall * pfnStartAddr) (void *),
                 void * pvParam,  unsigned dwCreateFlags,  unsigned *pdwThreadID)
    {
                 _ptiddata     ptd;         // Pointer to thread's data block
                 uintptr_t      thdl;         // Thread's handle
                 // Allocate data block for the new thread.
               if ((ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL)
                       goto   error_return; // Initialize the data block.
              initptd(ptd);             // Save the desired thread function and the parameter
              // we want it to get in the data block.
              ptd->_initaddr = (void *) pfnStartAddr;
              ptd->_initarg = pvParam;
              ptd->_thandle = (uintptr_t)(-1); // Create the new thread.
              thdl = (uintptr_t) CreateThread ((LPSECURITY_ATTRIBUTES)psa, cbStackSize, _threadstartex , (PVOID)         ptd, dwCreateFlags, pdwThreadID);
              if (thdl == 0) {
                        // Thread couldn't be created, cleanup and return failure.
                       goto error_return;
             } // Thread created OK, return the handle as unsigned long.
       return(thdl);
      error_return:
         // Error: data block or thread couldn't be created.
        // GetLastError() is mapped into errno corresponding values
        // if something wrong happened in CreateThread.
        _free_crt(ptd);
       return((uintptr_t)0L);
    }