标准CRT(C Runtime Library)问世于1970年,没有考虑到将CRT运行于多线程应用程序的问题。
- 在多线程环境中存在问题的C/C++运行期库变量和函数包括:error, _doserrno, strtok, strerror, asctime, gmtime,malloc等等。这些函数中都存在全局变量或者要访问全局变量(对于malloc来说,进程的堆也算是全全局的了),所以导致多个线程同时访问时,会发生互相影响。这下就会乱套了。
俗话说办法总比问题多,好在知道了问题之后,就会有解法了。
- 若要使多线程C/C++程序正确运行,必须创建一个数据结构,并将它与使用C/C++运行期库函数的每个线程关联起来。当调用这些库函数时,这些函数必须知道查看属于线程自己的数据块,这样就不会对别的线程产生影响。
既然系统不帮我们干,那么我们就自己干。(是系统不能干么?当然也不是,或许系统是为了让系统内核的接口保持简单而已,不要加那么多的逻辑在里面。在我们的工作当中不是也是么?比如一个特殊的数据格式转换,是在你的模块里做,还是在我的模块里面做,都可以,不过谁做了,谁的逻辑就稍微麻烦些了。)若要创建一个新线程,不要使用操作系统的CreateThread函数,必须调用C/C++运行期库函数_beginthreadex。
unsigned long _beginthreadex(
void *security,
unsinged stack_size,
unsigned (*start_address)(void *),
void *arglist,
unsinged initflag,
unsigned *thrdaddr_
);
- _beginthreadex函数的参数列表与CreateThread函数的参数列表是相同的,但是参数名和类型并不完全相同,据说是VC的C/C++运行期库的开发者认为不应该对Windows的数据类型有任何依赖。同样,返回参数为新创建线程的句柄。
- 我们知道VC的运行库分为单线程静态链接库、多线程静态链接库、多线程动态链接库(我在VC2013里面草草秒了一眼,貌似没有单线程静态链接库了,但是静态多线程连接库貌似有好几个版本了)。_beginthreadex函数只存在多线程的版本中,在使用多线程编译的时候,VC编译器会加上/MT参数。
- VC的运行库提供了源码(位置在Microsoft Visual Studio 12.0\VC\crt\src\threadex.c,我了个去,可以学习一下了同志们),一下是_beginthreadex的源代码:
/*
* CreateThread 包装
*/
static HANDLE _createThread(
LPSECURITY_ATTRIBUTES security,
unsigned stacksize,
LPVOID ptd,
unsigned createflag ,
LPDWORD thrdaddr)
{
return CreateThread(
security,
stacksize,
_threadstartex,
ptd,
createflag ,
thrdaddr);
}
_CRTIMP uintptr_t __cdecl _beginthreadex (
void * security,
unsigned stacksize,
unsigned ( __stdcall * initialcode ) (void *),
void * argument,
unsigned createflag ,
unsigned * thrdaddr
)
{
//这个ptd就是为每个线程分配数据块的指针*/
_ptiddata ptd; /* pointer to per-thread data */
uintptr_t thdl; /* thread handle */
unsigned long err = 0L; /* Return from GetLastError() */
unsigned dummyid; /* dummy returned thread ID,线程ID */
// 检查initialcode非空
/* validation section */
_VALIDATE_RETURN( initialcode != NULL , EINVAL , 0);
/* 申请一个ptd数据结构体,这个数据块对于将要创建线程来说是独有的
* Allocate and initialize a per-thread data structure for the to-
* be-created thread.
*/
if ( (ptd = (_ptiddata)_calloc_crt(1, sizeof