在windows下调用_beginthread创建子线程并获得子线程id(函数返回值),如果子线程很快退出,在主线程中调用WaitForSingleObject等待该线程id退出,会导致主线程卡死。需要修改_beginthread为_beginthreadex解决该问题。
那么,_beginthread为何会导致WaitForSingleObject卡死,而_beginthreadex却不会呢?这需要查看两个函数的实现。
历史原因
由于C/C++的历史早于线程的出现,因此C/C++的函数并不都是线程安全的。如全局变量errno等。
这就需要一种解决方案。一种方法是利用属于每个线程的数据块,该数据块不会被线程共享,而只能够用于线程自己,这样类似errno的情况便迎刃而解。
此外,C/C++运行库针对特定函数做了改写,使其能够进行线程同步。如malloc函数,由于不能够多线程同时执行内存堆分配操作,因此多线程版本的运行库进行了线程同步处理。
那么,如何让windows系统知道当我们创造新线程时,为我们分配属于线程的存储区呢?利用CreateThread函数并不行(C/C++运行库若获取不到存储器,会自动请求分配对应存储区,因此CreateThread函数实际也可以支持线程安全,但还有其他问题下面再说),因为他只是一个系统API,他不会知道你所写的是C\C++代码。
_beginthreadex函数
_beginthreadex是C/C++运行库创建线程函数,因此可以完美支持C/C++代码的线程安全。其声明如下:
uintptr_t _beginthreadex(void *security,
unsigned stack_size,
unsigned ( __stdcall*start_address )( void *),void *arglist,
unsigned initflag,
unsigned*thrdaddr
);
其参数意义与CreateThread函数完全相同。
重点是要理解该函数为C/C++线程安全做了那些事情。我们可以看到其函数定义。(VS2013路径为C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\threadex.c)
_CRTIMP uintptr_t __cdecl _beginthreadex (void *security,
unsigned stacksize,
unsigned (__stdcall* initialcode) (void *),void *argument,
unsigned createflag,
unsigned*thrdaddr
)
{
_ptiddata ptd;/*pointer to per-thread data*/uintptr_t thdl;/*thread handle*/unsignedlong err = 0L; /*Return from GetLastError()*/unsigned dummyid;/*dummy returned thread ID*/
/*validation section*/_VALIDATE_RETURN(initialcode!= NULL, EINVAL, 0);/** Allocate and initialize a per-thread data structure for the to-
* be-created thread.*/
if ( (ptd = (_p