/* 3:_beginthreadex()源码
*/
// 线程本地存储:TLS(Thread Local Storage)
// 每个线程都有自己专用的一块内存区域, 不共享
__declspec(thread) int g_nData = 0; //这就是两个线程都要访问的变量
// 获取线程数据结构指针
_ptiddata __cdecl _getptd(void)
{
// 1:尝试获得当前的ptd
// 2:如果为空,就重新创建一个
}
// _getptd_noexit() 与 _getptd()功能类似
// _getptd_noexit()函数首先通过TLS查找线程相关数据,如果没有找到,就分配一块内存,
// 存放_tiddata结构,并将这块内存与__flsindex相关联
_CRTIMP uintptr_t __cdecl _beginthreadex( void *security, unsigned stacksize,
unsigned (__stdcall * initialcode) (void *),
void * argument, unsigned createflag, unsigned *thrdaddr )
{
_ptiddata ptd; /* 线程数据结构指针*/
uintptr_t thdl; /* 线程句柄 */
ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata)); // 给线程数据结构分配内存
_initptd(ptd, _getptd()->ptlocinfo); // 初始化线程的_tiddata块即CRT数据区域
ptd->_initaddr = (void *) initialcode; // 线程函数1
ptd->_initarg = argument;
ptd->_thandle = (uintptr_t)(-1);
// 调用CreateThread()创建线程, 线程函数2是_threadstartex()
thdl = (uintptr_t)CreateThread( (LPSECURITY_ATTRIBUTES)security, stacksize,
_threadstartex,
(LPVOID)ptd, createflag, (LPDWORD)thrdaddr);
return(thdl);
}
static unsigned long WINAPI _threadstartex (void * ptd)
{
_callthreadstartex()
{
_ptiddata ptd = _getptd();
// 用户自定义线程函数地址
typedef unsigned (__CLR_OR_STD_CALL *PUSERTHREADPROC)(void *)
pUserThreadProc = (unsigned (__CLR_OR_STD_CALL *)(void *))( ((_ptiddata)ptd)->_initaddr );
void* pInitArg = (_ptiddata)ptd)->_initarg;
_endthreadex( pUserThreadProc(pInitArg)); // 执行用户自定义线程函数, 并结束线程
{
_ptiddata ptd = _getptd_noexit();
if (ptd)
{
_freeptd(ptd); // 释放
}
ExitThread(retcode);
}
}
}
/* 4:在CreateThread()创建的线程中使用 标准C运行库 中的函数 可能会导致内存泄露 的 原因
例如使用_errno()函数会调用_getptd_noexit()获取_tiddata指针, 但是CreateThread()创建的线程中
没有_tiddata, _getptd_noexit()就会重新申请内存, 然而CreateThread()中并没有处理这块内存的释放
*/
int * __cdecl _errno(void)
{
_ptiddata ptd = _getptd_noexit();
if (!ptd) {
return &ErrnoNoMem;
} else {
return ( &ptd->_terrno );
}
}
/* 5:避免CreateThread引发泄露,大致有几种方法:
1. 使用_beginthread/_beginthreadex函数创建线程
2. 在线程函数return前,显示调用_endthread/_endthreadex函数
3. 在线程函数return前,显示调用_freeptd(NULL),此方法在C语言中有效
4. 使用/MTd或/MDd参数
*/
/* 6:一定不要使用TerminateThread函数来终止线程
在连接静态库的情况下,TerminateThread会使线程直接被终止,因此没有释放ptd的机会;
在连接静态库的情况下,DLL_THREAD_DETACH只有在线程正常退出时才会产生, 因此被终止的线
程的ptd同样不会被释放.
*/
*/
// 线程本地存储:TLS(Thread Local Storage)
// 每个线程都有自己专用的一块内存区域, 不共享
__declspec(thread) int g_nData = 0; //这就是两个线程都要访问的变量
// 获取线程数据结构指针
_ptiddata __cdecl _getptd(void)
{
// 1:尝试获得当前的ptd
// 2:如果为空,就重新创建一个
}
// _getptd_noexit() 与 _getptd()功能类似
// _getptd_noexit()函数首先通过TLS查找线程相关数据,如果没有找到,就分配一块内存,
// 存放_tiddata结构,并将这块内存与__flsindex相关联
_CRTIMP uintptr_t __cdecl _beginthreadex( void *security, unsigned stacksize,
unsigned (__stdcall * initialcode) (void *),
void * argument, unsigned createflag, unsigned *thrdaddr )
{
_ptiddata ptd; /* 线程数据结构指针*/
uintptr_t thdl; /* 线程句柄 */
ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata)); // 给线程数据结构分配内存
_initptd(ptd, _getptd()->ptlocinfo); // 初始化线程的_tiddata块即CRT数据区域
ptd->_initaddr = (void *) initialcode; // 线程函数1
ptd->_initarg = argument;
ptd->_thandle = (uintptr_t)(-1);
// 调用CreateThread()创建线程, 线程函数2是_threadstartex()
thdl = (uintptr_t)CreateThread( (LPSECURITY_ATTRIBUTES)security, stacksize,
_threadstartex,
(LPVOID)ptd, createflag, (LPDWORD)thrdaddr);
return(thdl);
}
static unsigned long WINAPI _threadstartex (void * ptd)
{
_callthreadstartex()
{
_ptiddata ptd = _getptd();
// 用户自定义线程函数地址
typedef unsigned (__CLR_OR_STD_CALL *PUSERTHREADPROC)(void *)
pUserThreadProc = (unsigned (__CLR_OR_STD_CALL *)(void *))( ((_ptiddata)ptd)->_initaddr );
void* pInitArg = (_ptiddata)ptd)->_initarg;
_endthreadex( pUserThreadProc(pInitArg)); // 执行用户自定义线程函数, 并结束线程
{
_ptiddata ptd = _getptd_noexit();
if (ptd)
{
_freeptd(ptd); // 释放
}
ExitThread(retcode);
}
}
}
/* 4:在CreateThread()创建的线程中使用 标准C运行库 中的函数 可能会导致内存泄露 的 原因
例如使用_errno()函数会调用_getptd_noexit()获取_tiddata指针, 但是CreateThread()创建的线程中
没有_tiddata, _getptd_noexit()就会重新申请内存, 然而CreateThread()中并没有处理这块内存的释放
*/
int * __cdecl _errno(void)
{
_ptiddata ptd = _getptd_noexit();
if (!ptd) {
return &ErrnoNoMem;
} else {
return ( &ptd->_terrno );
}
}
/* 5:避免CreateThread引发泄露,大致有几种方法:
1. 使用_beginthread/_beginthreadex函数创建线程
2. 在线程函数return前,显示调用_endthread/_endthreadex函数
3. 在线程函数return前,显示调用_freeptd(NULL),此方法在C语言中有效
4. 使用/MTd或/MDd参数
*/
/* 6:一定不要使用TerminateThread函数来终止线程
在连接静态库的情况下,TerminateThread会使线程直接被终止,因此没有释放ptd的机会;
在连接静态库的情况下,DLL_THREAD_DETACH只有在线程正常退出时才会产生, 因此被终止的线
程的ptd同样不会被释放.
*/