//_tiddata的定义. CRT SRC\mtdll.h
struct _tiddata {
unsigned long _tid; /* thread ID */unsigned long _thandle; /* thread handle */int _terrno; /* errno value */unsigned long _tdoserrno; /* _doserrno value */unsigned int _fpds; /* Floating Point data segment */unsigned long _holdrand; /* rand() seed value */char * _token; /* ptr to strtok() token */#ifdef _WIN32wchar_t * _wtoken; /* ptr to wcstok() token */
#endif /* _WIN32 */
unsigned char * _mtoken; /* ptr to _mbstok() token *//* following pointers get malloc'd at runtime */
char * _errmsg; /* ptr to strerror()/_strerror() buff */char * _namebuf0; /* ptr to tmpnam() buffer */#ifdef _WIN32wchar_t * _wnamebuf0; /* ptr to _wtmpnam() buffer */
#endif /* _WIN32 */
char * _namebuf1; /* ptr to tmpfile() buffer */#ifdef _WIN32wchar_t * _wnamebuf1; /* ptr to _wtmpfile() buffer */
#endif /* _WIN32 */
char * _asctimebuf; /* ptr to asctime() buffer */#ifdef _WIN32wchar_t * _wasctimebuf; /* ptr to _wasctime() buffer */
#endif /* _WIN32 */
void * _gmtimebuf; /* ptr to gmtime() structure */char * _cvtbuf; /* ptr to ecvt()/fcvt buffer *//* following fields are needed by _beginthread code */
void * _initaddr; /* initial user thread address */void * _initarg; /* initial user thread argument *//* following three fields are needed to support signal handling and
* runtime errors */void * _pxcptacttab; /* ptr to exception-action table */void * _tpxcptinfoptrs; /* ptr to exception info pointers */int _tfpecode; /* float point exception code *//* following field is needed by NLG routines */
unsigned long _NLG_dwCode;/*
* Per-Thread data needed by C++ Exception Handling*/void * _terminate; /* terminate() routine */void * _unexpected; /* unexpected() routine */void * _translator; /* S.E. translator */void * _curexception; /* current exception */void * _curcontext; /* current exception context */#if defined (_M_MRX000)
void * _pFrameInfoChain;
void * _pUnwindContext;
void * _pExitContext;
int _MipsPtdDelta;
int _MipsPtdEpsilon;
#elif defined (_M_PPC)void * _pExitContext;
void * _pUnwindContext;
void * _pFrameInfoChain;
int _FrameInfo[6];
#endif /* defined (_M_PPC) */
};typedef struct _tiddata * _ptiddata;
//_getptd _freeptd 的相关实现. CRT SRC\tidtable.c
/***
*tidtable.c - Access thread data table** Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.**Purpose:* This module contains the following routines for multi-thread* data support:** _mtinit = Initialize the mthread data* _getptd = get the pointer to the per-thread data structure for* the current thread* _freeptd = free up a per-thread data structure and its* subordinate structures* __threadid = return thread ID for the current thread* __threadhandle = return pseudo-handle for the current thread********************************************************************************/#if defined (_MT)
# if defined(_NTSUBSET_)
#include <nt.h>#include <ntrtl.h>#include <nturtl.h>#include <ntstatus.h>#include <ntos.h>#include <fsrtl.h># endif /* _NTSUBSET_ */
#include <cruntime.h>#include <oscalls.h>#include <internal.h>#include <mtdll.h>#include <memory.h>#include <msdos.h>#include <rterr.h>#include <stdlib.h>#include <stddef.h>#include <dbgint.h>unsigned long __tlsindex = 0xffffffff;/****
*_mtinit() - Init multi-thread data bases**Purpose:* (1) Call _mtinitlocks to create/open all lock semaphores.* (2) Allocate a TLS index to hold pointers to per-thread data* structure.** NOTES:* (1) Only to be called ONCE at startup* (2) Must be called BEFORE any mthread requests are made**Entry:* <NONE>*Exit:* returns on success* calls _amsg_exit on failure**Uses:* <any registers may be modified at init time>**Exceptions:********************************************************************************/int __cdecl _mtinit (
void
){_ptiddata ptd;/*
* Initialize the mthread lock data base*/_mtinitlocks();/*
* Allocate a TLS index to maintain pointers to per-thread data*/if ( (__tlsindex = TlsAlloc()) == 0xffffffff )
return FALSE; /* fail to load DLL *//*
* Create a per-thread data structure for this (i.e., the startup)* thread.*/if ( ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) == NULL) ||!TlsSetValue(__tlsindex, (LPVOID)ptd) )return FALSE; /* fail to load DLL *//*
* Initialize the per-thread data*/_initptd(ptd);ptd->_tid = GetCurrentThreadId();ptd->_thandle = (unsigned long)(-1L);return TRUE;
}/****
*_mtterm() - Clean-up multi-thread data bases**Purpose:* (1) Call _mtdeletelocks to free up all lock semaphores.* (2) Free up the TLS index used to hold pointers to* per-thread data structure.** NOTES:* (1) Only to be called ONCE at termination* (2) Must be called AFTER all mthread requests are made**Entry:* <NONE>*Exit:* returns**Uses:**Exceptions:********************************************************************************/void __cdecl _mtterm (
void
){/*
* Clean up the mthread lock data base*/_mtdeletelocks();/*
* Free up the TLS index** (Set the variable __tlsindex back to the unused state (-1L).)*/if ( __tlsindex != 0xffffffff ) {
TlsFree(__tlsindex);__tlsindex = 0xffffffff;}}/***
*void _initptd(_ptiddata ptd) - initialize a per-thread data structure**Purpose:* This routine handles all of the per-thread initialization* which is common to _beginthread, _beginthreadex, _mtinit* and _getptd.**Entry:* pointer to a per-thread data block**Exit:* the common fields in that block are initialized**Exceptions:********************************************************************************/void __cdecl _initptd (
_ptiddata ptd){ptd->_pxcptacttab = (void *)_XcptActTab;
ptd->_holdrand = 1L;#ifdef _M_MRX000/*
* MIPS per-thread data*/ptd->_MipsPtdDelta =ptd->_MipsPtdEpsilon = -1L ;#endif /* _M_MRX000 */
}/***
*_ptiddata _getptd(void) - get per-thread data structure for the current thread**Purpose:**Entry:* unsigned long tid**Exit:* success = pointer to _tiddata structure for the thread* failure = fatal runtime exit**Exceptions:********************************************************************************/_ptiddata __cdecl _getptd (void
){_ptiddata ptd;DWORD TL_LastError;TL_LastError = GetLastError();if ( (ptd = TlsGetValue(__tlsindex)) == NULL ) {
/*
* no per-thread data structure for this thread. try to create* one.*/if ( ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) != NULL) &&TlsSetValue(__tlsindex, (LPVOID)ptd) ) {/*
* Initialize of per-thread data*/_initptd(ptd);ptd->_tid = GetCurrentThreadId();ptd->_thandle = (unsigned long)(-1L);}else
_amsg_exit(_RT_THREAD); /* write message and die */
}SetLastError(TL_LastError);return(ptd);
}/***
*void _freeptd(_ptiddata) - free up a per-thread data structure**Purpose:* Called from _endthread and from a DLL thread detach handler,* this routine frees up the per-thread buffer associated with a* thread that is going away. The tiddata structure itself is* freed, but not until its subordinate buffers are freed.**Entry:* pointer to a per-thread data block (malloc-ed memory)* If NULL, the pointer for the current thread is fetched.**Exit:**Exceptions:********************************************************************************/void __cdecl _freeptd (
_ptiddata ptd){/*
* Do nothing unless per-thread data has been allocated for this module!*/if ( __tlsindex != 0xFFFFFFFF ) {
/*
* if parameter "ptd" is NULL, get the per-thread data pointer* Must NOT call _getptd because it will allocate one if none exists!*/if ( ! ptd )
ptd = TlsGetValue(__tlsindex );/*
* Free up the _tiddata structure & its malloc-ed buffers.*/if ( ptd ) {
if(ptd->_errmsg)
_free_crt((void *)ptd->_errmsg);
if(ptd->_namebuf0)
_free_crt((void *)ptd->_namebuf0);
if(ptd->_namebuf1)
_free_crt((void *)ptd->_namebuf1);
if(ptd->_asctimebuf)
_free_crt((void *)ptd->_asctimebuf);
if(ptd->_gmtimebuf)
_free_crt((void *)ptd->_gmtimebuf);
if(ptd->_cvtbuf)
_free_crt((void *)ptd->_cvtbuf);
if (ptd->_pxcptacttab != _XcptActTab)
_free_crt((void *)ptd->_pxcptacttab);
_free_crt((void *)ptd);
}/*
* Zero out the one pointer to the per-thread data block*/TlsSetValue(__tlsindex, (LPVOID)0);}}/***
*__threadid() - Returns current thread ID*__threadhandle() - Returns "pseudo-handle" for current thread**Purpose:* The two function are simply do-nothing wrappers for the corresponding* Win32 APIs (GetCurrentThreadId and GetCurrentThread, respectively).**Entry:* void**Exit:* thread ID value**Exceptions:********************************************************************************/_CRTIMP unsigned long __cdecl __threadid (void
){return( GetCurrentThreadId() );
}_CRTIMP unsigned long __cdecl __threadhandle(void
){return( (unsigned long)GetCurrentThread() );}#endif /* defined (_MT) */
以下来自:http://www.cnblogs.com/chinasasu/archive/2010/04/11/1709767.html,节选。
或许有人会说,我用CreateThread创建线程以后,也调用了C运行库函数,并且也使用ExitThread退出了,可是我的程序运行得好好的,既
没有因为CRT没有初始化而崩溃,也没有因为忘记调用_endthread而发生内存泄漏,这是为什么呢,让我们继续我们的CRT之旅。
假设我用CreateThread创建了一个线程,我调用strtok函数来进行字符串处理,这个函数肯定是需要某些额外的运行时支持的。strtok的源代码在strtok.c中。从代码可见,在多线程情况下,strtok的第一句有效代码就是_ptiddata ptd = _getptd(),它通过这个来获得当前的ptd。可是我们并没有通过_beginthread来创建ptd,那么一定是_getptd捣鬼了。打开tidtable.c,可以看到_getptd的实现,果然,它先尝试获得当前的ptd,如果不能,就重新创建一个,因此,后续的CRT调用就安全了。可是这块ptd最终又是谁释放的呢?打开dllcrt0.c,可以看到一个DllMain函数。在VC中,CRT既可以作为一个动态链接库和主程序链接,也可以作为一个静态库和主程序链接,这个在Project Setting->Code Generations里面可以选。当CRT作为DLL链接到主程序时,DllMain就是CRT DLL的入口。Windows的DllMain可以由四种原因调用:Process Attach/Process Detach/Thread Attach/Thread Detach,最后一个,也就是当线程函数退出后但是线程还没有销毁前,会在这个线程的上下文中用Thread Detach调用DllMain,这里,CRT做了一个_freeptd(NULL),也就是说,如果有ptd,就free掉。所以说,恰巧没有发生内存泄漏是因为你用的是动态链接的CRT。
于是我们得出了一个更精确的结论,如果我没有使用那些会使用_getptd的CRT函数,使用CreateThread就是安全的。