<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 如何在C/C++運行庫下正確的啟用新線程 由于C/C++的歷史早于線程的出現,因此C/C++的函數并不都是線程安全的。如全局變量errno等。 這就需要一種解決方案。一種方法是利用屬于每個線程的數據塊,該數據塊不會被線程共享,而只能夠用于線程自己,這樣類似errno的情況便迎刃而解。 此外,C/C++運行庫針對特定函數做了改寫,使其能夠進行線程同步。如malloc函數,由于不能夠多線程同時執行內存堆分配操作,因此多線程版本的運行庫進行了線程同步處理。 那么,如何讓windows系統知道當我們創造新線程時,為我們分配屬于線程的存儲區呢?利用CreateThread函數并不行(C/C++運行庫若獲取不到存儲器,會自動請求分配對應存儲區,因此CreateThread函數實際也可以支持線程安全,但還有其他問題下面再說),因為他只是一個系統API,他不會知道你所寫的是C\C++代碼。 ## _beginthreadex函數 _beginthreadex是C/C++運行庫創建線程函數,因此可以完美支持C/C++代碼的線程安全。其聲明如下: ~~~ uintptr_t _beginthreadex( void *security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr ); ~~~ 其參數意義與CreateThread函數完全相同。 重點是要理解該函數為C/C++線程安全做了那些事情。我們可以看到其函數定義。(VS2012路徑為C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\crt\src\threadex.c) ~~~ _CRTIMP uintptr_t __cdecl _beginthreadex ( void *security, unsigned stacksize, unsigned (__stdcall * initialcode) (void *), void * argument, unsigned createflag, unsigned *thrdaddr ) { _ptiddata ptd; /* pointer to per-thread data */ uintptr_t thdl; /* thread handle */ unsigned long err = 0L; /* Return from GetLastError() */ unsigned dummyid; /* dummy returned thread ID */ /* validation section */ _VALIDATE_RETURN(initialcode != NULL, EINVAL, 0); /* * 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; ptd->_thandle = (uintptr_t)(-1); #if defined (_M_CEE) || defined (MRTDLL) if(!_getdomain(&(ptd->__initDomain))) { goto error_return; } #endif /* defined (_M_CEE) || defined (MRTDLL) */ /* * Make sure non-NULL thrdaddr is passed to CreateThread */ if ( thrdaddr == NULL ) thrdaddr = &dummyid; /* * Create the new thread using the parameters supplied by the caller. */ if ( (thdl = (uintptr_t) CreateThread( (LPSECURITY_ATTRIBUTES)security, stacksize, _threadstartex, (LPVOID)ptd, createflag, (LPDWORD)thrdaddr)) == (uintptr_t)0 ) { 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. * * Note: this routine returns 0 for failure, just like the Win32 * API CreateThread, but _beginthread() returns -1 for failure. */ if ( err != 0L ) _dosmaperr(err); return( (uintptr_t)0 ); } ~~~ **可以看到_beginthreadex函數做了以下事項:** 1、在函數開始處,在C/C++運行庫堆上分配并初始化每個線程的私有內存ptd。 2、我們初始傳入的線程函數與線程參數被存儲到ptd中。 3、_beginthreade最終調用CreateThread函數運行線程(畢竟windows系統只認識其API)。 4、注意在CreateThread函數中,線程函數替換為另一函數_threadstartex,同時線程參數傳入了ptd。 ## _threadstartex函數 由_beginthreadex函數定義可以知道,我們的線程函數,其實首先執行的都是_threadstartex。那么我們看看該函數都做了什么。 ~~~ static unsigned long WINAPI _threadstartex ( void * ptd ) { _ptiddata _ptd; /* pointer to per-thread data */ /* * Check if ptd is initialised during THREAD_ATTACH call to dll mains */ if ( ( _ptd = (_ptiddata)__crtFlsGetValue(__get_flsindex())) == NULL) { /* * Stash the pointer to the per-thread data stucture in TLS */ if ( !__crtFlsSetValue(__get_flsindex(), ptd) ) ExitThread(GetLastError()); /* * Set the thread ID field -- parent thread cannot set it after * CreateThread() returns since the child thread might have run * to completion and already freed its per-thread data block! */ ((_ptiddata) ptd)->_tid = GetCurrentThreadId(); _ptd = ptd; } else { _ptd->_initaddr = ((_ptiddata) ptd)->_initaddr; _ptd->_initarg = ((_ptiddata) ptd)->_initarg; _ptd->_thandle = ((_ptiddata) ptd)->_thandle; #if defined (_M_CEE) || defined (MRTDLL) _ptd->__initDomain=((_ptiddata) ptd)->__initDomain; #endif /* defined (_M_CEE) || defined (MRTDLL) */ _freefls(ptd); ptd = _ptd; } #if defined (_M_CEE) || defined (MRTDLL) DWORD domain=0; if(!_getdomain(&domain)) { ExitThread(0); } if(domain!=_ptd->__initDomain) { /* need to transition to caller's domain and startup there*/ ::msclr::call_in_appdomain(_ptd->__initDomain, _callthreadstartex); return 0L; } #endif /* defined (_M_CEE) || defined (MRTDLL) */ _ptd->_initapartment = __crtIsPackagedApp(); if (_ptd->_initapartment) { _ptd->_initapartment = _initMTAoncurrentthread(); } _callthreadstartex(); /* * Never executed! */ return(0L); } ~~~ **上面代碼很多,大體看下就好。要了解的是:** 1、和往常一樣,CreateThread后,系統會先調用RtlUserThreadStart,然后由其調用_threadstartex。 2、在_threadstartex中,調用了系統API [TlsSetValue](https://msdn.microsoft.com/en-us/library/aa908728.aspx) 來講ptd與調用線程關聯起來(TLS 線程本地存儲)。 3、_threadstartex調用 ?_callthreadstartex() 來運行我們最初傳入的線程函數。 ## ?_callthreadstartex函數 經歷了上面種種,最終我們傳入的線程函數,會被?_callthreadstartex函數調用。其定義如下: ~~~ static void _callthreadstartex(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 { _endthreadex ( ( (unsigned (__CLR_OR_STD_CALL *)(void *))(((_ptiddata)ptd)->_initaddr) ) ( ((_ptiddata)ptd)->_initarg ) ) ; } __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) ) { /* * Should never reach here */ _exit( GetExceptionCode() ); } /* end of _try - _except */ } ~~~ 該函數很簡單,就是拿出ptd的值,同時,調用了_endthreadex函數來執行并返回我們的線程函數。? ## _endthreadex函數 與_beginthreadex函數對應,_endthreadex是C/C++運行庫終止線程運行的函數,其定義如下 ~~~ /*** *_endthreadex() - Terminate the calling thread * *Purpose: * *Entry: * Thread exit code * *Exit: * Never returns! * *Exceptions: * *****************************************/ void __cdecl _endthreadex ( unsigned retcode ) { _ptiddata ptd; /* pointer to thread's _tiddata struct */ ptd = _getptd_noexit(); if (ptd) { if (ptd->_initapartment) _uninitMTAoncurrentthread(); /* * 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(retcode); } ~~~ **與_beginthreadex函數對應,** 1、_endthreadex銷毀了在_beginthreadex分配的堆內存(保證了沒有內存泄露)。 2、其調用了系統API ExitThread退出線程。 ## ExitThread? VS _endthreadex 在編寫C\C++程序時,要調用_endthreadex來結束線程。基于如下兩個理由: 1、ExitThread函數非C++函數,線程創建的C++對象不會得到析構。 2、若線程中使用了ptd,ExitThread不會釋放內存,造成內存泄露。 ## CreateThread VS _beginthreadex 一般的理由是,CreateThread有可能照成內存泄露。(如果使用了ptd內存,而CreateThread并不會在內部自動調用釋放內存函數,但若鏈接的是C/C++運行庫的dll版本,則其會在線程退出的DLL_THREAD_DETCH通知中釋放內存)。 ## 不要調用的C/C++函數 _beginthreadex和_endthreadex分別有兩個比較老的版本: ~~~ uintptr_t _beginthread( void( __cdecl *start_address )( void * ), unsigned stack_size, void *arglist ); void _endthread( void ); ~~~ 我們應該忘記這兩個函數,不要調用它們。 對于_beginthread函數,可以看出其函數參數是較少的,例如其中不包括安全屬性,讓我們對線程的控制力沒有其增強版本多。 同時,由于在_beginthread內部會調用_endthread函數,而該函數多此一舉的會調用一次CloseHandle,來幫我們關閉線程句柄。似乎有友好的操作,但實際會照成問題。例如下邊代碼 ~~~ HANDLE hThread = _beginthread(...); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); ~~~ 在真正調用WaitForSingleObject之前,_beginthread函數里的線程可能已經執行完畢,系統會釋放一次線程handle句柄,同時,_endthread又會釋放一次handle句柄(此時handle計數為0)。那么再調用WaitForSingleObject時,可能這時的hThread已經是一個無效句柄,導致函數調用失敗,同理,對CloseHandle也是一樣。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看