posts - 0,  comments - 0,  trackbacks - 0

CreateThread: Windows API函数。用于创建一个线程。
---------------------------------------------------------------------------------------------
         HANDLE CreateThread(
                PSECURITY_ATTRIBUTES psa,
                DWORD cbStackSize,
                PTHREAD_START_ROUTINE pfnStartAddr,
                PVOID pvParam,
                DWORD dwCreateFlags,
                PDWORD pdwThreadID);
---------------------------------------------------------------------------------------------

调用CreateThread函数之后,系统要做的事情:

1. 系统创建一个线程内核对象。该对象最初的使用计数为2,其他属性也被初始化:暂停计数被设为1,退出代码被设为STILL_ACTIVE(0x103),而且对象被设为未触发状态。

2. 系统分配内存,供线程的堆栈使用。系统将两个值写入新线程堆栈的最上端:第一个值是传给CreateThread函数的pvParam参数的值。紧接在它下方的是传给CreateThread函数的pfnStartAddr的值。

3. 系统将检查CREATE_SUSPENDED标志是否已经被传给CreateThread函数。

4. 线程可以调试给一个处理器去执行。

线程内核对象中保存有一个CONTEXT结构。每个线程都有其自己的一组CPU寄存器,称为线程的上下文(context)。线程的CPU寄存器全部保存在一个CONTEXT结构中。
指令指针寄存器(IP)和栈指针寄存器(SP)是线程上下文中最重要的两个寄存器。栈指针寄存器被设为pfnStartAddr在线程堆栈中的地址,而指令指针寄存器被设为RtlUserThreadStart函数(NTDLL.dll导出的)。

RtlUserThreadStart函数是线程开始执行的函数,它在函数体内调用pfnStartAddr函数(即由CreateThread指定的线程函数),最后调用ExitThread(将线程函数pfnStartAddr的返回值传给它)终止线程。

● _beginthreadex: C/C++运行库函数。用于创建一个线程。
---------------------------------------------------------------------------------------------
unsigned long _beginthreadex(
      void *security,
      unsigned stack_size,
      unsigned (*start_address)(void *),
      void *arglist,
      unsigned initflag,
      unsigned *thrdaddr);
---------------------------------------------------------------------------------------------

为了保证调用C/C++运行库函数的C/C++多线程应用程序正常运行,必须创建一个数据结构,并使之与使用了C/C++运行库函数的每个线程关联。_tiddata结构就是为了这个目的产生的。

对于_beginthreadex函数,有如下要点:

1. 每个线程都会有自己专用的_tiddata内存块(_tiddata结构),它们是从C/C++运行库的堆中分配的。

2. 传给_beginthreadex的线程函数的地址被保存在_tiddata内存块中。

3. _beginthreadex确实会在内部调用CreateThread,因为操作系统只知道用这种方式来创建一个新线程。

4. CreateThread函数被调用时,传给它的函数地址是_threadstartex(而非pfnStartAddr)。另外,参数地址是_tiddata结构的地址,而非pvParam

为什么创建新线程时建议使用_beginthreadex而不是CreateThread?

当一个线程调用一个需要_tiddata结构的C/C++运行库函数时,会发生下面的情况(大多数C/C++运行库函数都是线程安全的,不需要这个结构):
首先,C/C++运行库函数尝试取得线程数据块的地址(通过调用TlsGetValue)。如果NULL被作为_tiddata块的地址返回,表明主调用线程没有与这关联的_tiddata块,在这个时候,C/C++运行库函数会为主调用线程分配并初始化一个_tiddata块。然后,这个块会与线程关联(通过TlsSetValue),而且只要线程还在运行,这个块就会一直存在并与线程关联。现在,C/C++运行库函数可以使用线程的_tiddata块,以后调用的任何C/C++运行库函数也都可以使用。

当然,这是相当诱人的,因为线程(几乎)可以顺畅运行。但事实上,问题还是有的:

1. 假如线程使用了C/C++运行库的signal函数,则整个进程都会终止。因为结构化异常处理(SEH)帧没有就绪。
2. 假如线程不是通过调用_endthreadex来终止的,数据块就不能被销毁,从而导致内存泄漏。(对于一个用CreateThread函数来创建的线程,谁会调用_endthreadex呢?)

阅读全文
类别:visual c++ 查看评论
posted on 2010-10-14 16:57 ewook 阅读(211) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理