vc++下新建线程
vc++新建线程,可以使用CreateThread,这是windows API提供的接口,还有C运行时库提供的接口为_beginthread和_beginthreadex。
在编写代码的过程中,如果用到c库提供的接口,那么就会去读写一些c库提供的全局变量,例如错误值。
在线程中使用C库接口时,如果当前线程没有独立的_ptiddata结构,那么会申请内存存放此数据,如下:
当使用_errno变量时,
error是宏定义,含义为从_error()函数返回的地址取值。
而函数_error()实现如下:
在dosmap.c文件中,调用_getptd_noexit函数获取_ptiddata数据指针,_getptd_noexit 函数代码如下,这是vs 2013带的库
_ptiddata __cdecl _getptd_noexit ( void ) { _ptiddata ptd; DWORD TL_LastError; TL_LastError = GetLastError(); if ( (ptd = __crtFlsGetValue(__flsindex)) == NULL ) { /* * no per-thread data structure for this thread. try to create * one. */ #ifdef _DEBUG extern void * __cdecl _calloc_dbg_impl(size_t, size_t, int, const char *, int, int *); if ((ptd = _calloc_dbg_impl(1, sizeof(struct _tiddata), _CRT_BLOCK, __FILE__, __LINE__, NULL)) != NULL) { #else /* _DEBUG */ if ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) != NULL) { #endif /* _DEBUG */ if (__crtFlsSetValue(__flsindex, (LPVOID)ptd) ) { /* * Initialize of per-thread data */ _initptd(ptd,NULL); ptd->_tid = GetCurrentThreadId(); ptd->_thandle = (uintptr_t)(-1); } else { /* * Return NULL to indicate failure */ _free_crt(ptd); ptd = NULL; } } } SetLastError(TL_LastError); return(ptd); }
代码在tidtable.c文件中。在此函数中使用TLS线程局部存储技术,来保存_ptiddata数据指针。如果没有此数据,那么调用_calloc_crt申请内存空间,再设置tls。
当调用CreateThread时,使用c语言接口时,就会自动申请此内存地址,某些情况下会造成内存泄露,可参考微软文档https://learn.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitthread ,在某些情况下又不会造成内存泄露,还需要进行测试。
而使用_beginthread和_beginthreadex时,下面代码是使用_beginthread,传入StartAddress函数地址。
可以看到调用栈如上,当StartAddress结束时,会返回到_callthreadstart函数,_callthreadstart函数代码如下:
static void _callthreadstart(void) { _ptiddata ptd; /* pointer to thread's _tiddata struct */ /* must always exist at this point */ ptd = _getptd(); /* * Guard call to user code with a _try - _except statement to * implement runtime errors and signal support */ __try { ( (void(__CLRCALL_OR_CDECL *)(void *))(((_ptiddata)ptd)->_initaddr) ) //此处调用用户传入的函数地址,传入一个参数 ( ((_ptiddata)ptd)->_initarg ); _endthread(); } __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) ) { /* * Should never reach here */ _exit( GetExceptionCode() ); } /* end of _try - _except */ }
__try块中调用用户定义的线程入口函数,之后调用_endthread()函数,_endthread函数代码如下:
void __cdecl _endthread ( void ) { _ptiddata ptd; /* pointer to thread's _tiddata struct */ ptd = _getptd_noexit(); if (ptd) { /* * Close the thread handle (if there was one) */ if ( ptd->_thandle != (uintptr_t)(-1) ) (void) CloseHandle( (HANDLE)(ptd->_thandle) ); /* * Free up the _tiddata structure & its subordinate buffers * _freeptd() will also clear the value for this thread * of the FLS variable __flsindex. */ _freeptd(ptd); } /* * Terminate the thread */ ExitThread(0); }
_endthread函数中会调用_freeptd释放_ptiddata数据块。
所以说使用createthread时,在线程中调用c库函数可能会造成内存泄露,而使用_beginthread和_beginthreadex不会。