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不会。

 

posted @ 2023-02-01 17:15  psj00  阅读(310)  评论(0编辑  收藏  举报