● 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) 编辑 收藏 引用