注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Allen小笔记

有时会忘记努力...

 
 
 

日志

 
 

CreateThread和_beginThread有什么不同  

2009-11-30 11:54:28|  分类: Windows |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
  在Windows上创建一个线程的方法有两种:
1.直接调用OS的API(Application Programming Interface,不懂英文的看这里)函数:
   HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,    __in       SIZE_T dwStackSize,    __in       LPTHREAD_START_ROUTINE lpStartAddress,    __in_opt   LPVOID lpParameter,    __in       DWORD dwCreationFlags,    __out_opt  LPDWORD lpThreadId  );
这个函数的参数,就如同他的命名一样,一眼就看的懂。没有太多的讲头。
2.使用CRT(C Runtime Library)的函数:
uintptr_t _beginthread(      void( *start_address )( void * ),     unsigned stack_size,     void *arglist   );  uintptr_t _beginthreadex(      void *security,     unsigned stack_size,     unsigned ( *start_address )( void * ),     void *arglist,     unsigned initflag,     unsigned *thrdaddr   );
同样的,我也不说明参数的含义,只说明一下两者的不同。
The routine at start_addressmust use the __cdecl calling convention and should have no return value if _beginthread used.
这句话的意思就是说:如果你使用_beginthread,那么线程函数原型就必须是__cdecl void ThreadFunc(LPVOID lpParam)这样的;
The routine at start_address passed to _beginthreadex must use the __stdcall calling convention and must return a thread exit code.
而这句话的意思就是说:如果你使用_beginthreadex,那么线程函数的原型就必须是__stdcall unsigned  ThreadFunc(LPVOID lpParam)这样的;
而且_beginthreadex给你更多的控制权力,更加的safe(当使用synchronization APIs的时候,还有当线程函数会很快退出的情况下).
很容易的就可以看出来这两个函数参数是多么的相似。对,_beginthread就是CreateThread的包装,让我们来仔细看一下_beginthread函数。(在我的电脑上的路径是:C:\Program Files\Microsoft Visual Studio 9.0\VC\crt\src\thread.c)
_MCRTIMP uintptr_t __cdecl _beginthread (
        void (__CLRCALL_OR_CDECL * initialcode) (void *),
        unsigned stacksize,
        void * argument
        )
{
        _ptiddata ptd;                  /* pointer to per-thread data */
        uintptr_t thdl;                 /* thread handle */
        unsigned long err = 0L;     /* Return from GetLastError() */

        /* validation section */
        _VALIDATE_RETURN(initialcode != NULL, EINVAL, -1);

        /* Initialize FlsGetValue function pointer */
        __set_flsgetvalue();

        /*
         * Allocate and initialize a per-thread data structure for the to-
         * be-created thread.
         */
        if ( (ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL )
        {
            goto error_return;
        }

        /*
         * Initialize the per-thread data
         */

        _initptd(ptd, _getptd()->ptlocinfo);

        ptd->_initaddr = (void *) initialcode;
        ptd->_initarg = argument;

#if defined (_M_CEE) || defined (MRTDLL)
        if(!_getdomain(&(ptd->__initDomain)))
        {
            goto error_return;
        }
#endif  /* defined (_M_CEE) || defined (MRTDLL) */

        /*
         * Create the new thread. Bring it up in a suspended state so that
         * the _thandle and _tid fields are filled in before execution
         * starts.
         */
        if ( (ptd->_thandle = thdl = (uintptr_t)
              CreateThread( NULL,
                            stacksize,
                            _threadstart,
                            (LPVOID)ptd,
                            CREATE_SUSPENDED,
                            (LPDWORD)&(ptd->_tid) ))
             == (uintptr_t)0 )
        {
                err = GetLastError();
                goto error_return;
        }

        /*
         * Start the new thread executing
         */
        if ( ResumeThread( (HANDLE)thdl ) == (DWORD)(-1) ) {
                err = GetLastError();
                goto error_return;
        }

        /*
         * Good return
         */
        return(thdl);

        /*
         * Error return
         */
error_return:
        /*
         * Either ptd is NULL, or it points to the no-longer-necessary block
         * calloc-ed for the _tiddata struct which should now be freed up.
         */
        _free_crt(ptd);

        /*
         * Map the error, if necessary.
         */
        if ( err != 0L )
                _dosmaperr(err);

        return( (uintptr_t)(-1) );
}
源码面前一览无余!重要的部分我已经用红色的Mark起来了,可以看出来了吧, _beginthread会申请一块_tiddata的内存。然后调用CreateThread()这只API函数。所以,对啊,_tiddata只有CRT知道它的存在,OS 的API怎么可能会知道呢?再然后,谁去释放它呢?当然只能是知道它存在的CRT咯。所以要用_endthread去free.不信?RTFS!
      void __cdecl _endthread (
        void
        )
{
        _ptiddata ptd;           /* pointer to thread's _tiddata struct */

        /*
         * Call fp termination, if necessary
         */
#ifdef CRTDLL
        _fpclear();
#else  /* CRTDLL */
        if (_FPmtterm != NULL &&
            _IsNonwritableInCurrentImage((PBYTE)&_FPmtterm))
        {
            (*_FPmtterm)();
        }
#endif  /* CRTDLL */

        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);
}
已经用红色Mark起来了。
总结,程序里调用了CRT的函数,不管是直接还是间接,你就老老实实用_beginThread,否则两者都okay。

补遗:
上面所说的_tiddata的structure,_beginthread用_initptd()初始化_tiddata之后传递给_beginthread自己的线程入口函数_threadstart显示的将_tiddata保存到TLS(Thread Local Storage)数组中.(题外话,我们自己的线程函数就是保存在_tiddata中),再然后就是调用我们的线程函数啦。RTFS!
static unsigned long WINAPI _threadstart (void * ptd)
{
        _ptiddata _ptd;                  /* pointer to per-thread data */
        /* Initialize FlsGetValue function pointer */
        __set_flsgetvalue();
        if ( (_ptd = (_ptiddata)__fls_getvalue(__get_flsindex())) == NULL)
        {
            /*
             * Stash the pointer to the per-thread data stucture in TLS
             */
            if ( !__fls_setvalue(__get_flsindex(), ptd) )
            {
                ExitThread(GetLastError());
            }
        }
       .
       省略....
       .
        _callthreadstart();
        return(0L);
}
_callthreadstart();就是thread自己的线程函数。
 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 */
}
红色字体部分就是调用我们传入的线程函数,执行之后就是_endthread()的调用。这里我们还可以看到,他使用了__try/__catch处理。

现在我们假设用CreateThread去创建一个线程,然后在线程函数里面调用一个CRT函数,那么基本上那个CRT函数会去创建一个_tiddata的TLS变量。如果使用ExitThread就有可能会内存泄漏,那为什么是有可能呢?
  1. 内存泄漏:用的是静态的CRT
  2. 内存不泄漏:用的是动态的CRT
动态的CRT之所以不会泄漏,是因为,DLLMain这个函数,当DLL被DLL_PROCESS_DETACH的时候,会有机会释放_tiddata.
  评论这张
 
阅读(993)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017