Windows線程生滅 (一)關於_BEGINTHREADEX、_BEGINTHREAD和CREATETHREADMFC 多線程及線程同步

來源:互聯網
上載者:User
文章目錄
  • 1. _beginthread與_beginthreadex
  • 2. AfxBeginThread與mfc的CreateThread

一、線程建立

Windows線程在建立時會首先建立一個線程核心對象,它是一個較小的資料結構,作業系統通過它來管理線程。新線程可以訪問進程核心對象的所有控制代碼、進程中的所有記憶體及同一進程中其它線程的棧。

建立有以下幾種方式,分別說明

  1. CreateThread(...) (作業系統提供的API,盡量不要使用)
  2. _beginthread(...)
  3. _beginthreadex(...)
  4. AfxBeginThread(...) (MFC提供的介面)

首先聲明一個線程函數,原型為:

DWORD FunThread(LPVOID pParam);
1. CreateThread()

該函數為作業系統提供,原型如下:

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 );

說明:

Header Library Dll
WinBase.h Kernel32.lib Kernel32.dll

 

 

參數:

  lpThreadAttributes:指向SECURITY_ATTRIBUTES結構體的指標,記錄線程的安全描述。決定子進程能否繼承到返回的控制代碼,如果為NULL,則採用預設安全層級(THREAD_PRIORITY_NORMAL),同時返回控制代碼不能繼承
  dwStackSize:指定線程棧大小,當為0時,表示棧使用預設大小
  lpStartAddress:線程函數指標
  lpParameter:線程函數參數
  dwCreationFlags:為0:表示線程建立後立即運行;為CREATE_SUSPEND:建立後掛起,此時可修改線程屬性,通過ResumeThread喚醒;
  lpThreadId:一個指向threadID的指標,若對線程ID關注,則傳值,否則置NULL

傳回值:

  建立線程的控制代碼;

  若建立失敗,則返回NULL,可用GetLastError()捕獲錯誤;

MFC中也提供了CreateThread函數,它是CWinThread類的一個方法,如下

BOOL CreateThread(   DWORD dwCreateFlags = 0,   UINT nStackSize = 0,   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

參數含義與傳回值含義一致,它的調用方式是:

CWinThread thread1;thread1.CreateThread();

需要說明的是dwCreateFlags傳值為CREATE_SUSPEND時, 要通過CWinThread::ResumeThread來喚醒

2. _beginthread(), _beginthreadex()

原型:

unsigned long _beginthread(         void( __cdecl *start_address )( void * ),         unsigned stack_size, void *arglist );unsigned long _beginthreadex(         void *security,         unsigned stack_size,         unsigned ( __stdcall *start_address )( void * ),         void *arglist,         unsigned initflag,         unsigned *thrdaddr );            

說明:

Header Library
process.h LIBCMT.lib  MSVCRT.lib 

 

 

參數與上面CreateThread含義相同,不在贅述;

二者比較:

1. _beginthread中線程函數調用為_cdecl,且無傳回值; _beginthreadex為_stdcall,有傳回值;

2. _beginthreadex中initflag相當於CreateThread中的dwCreationFlags,thrdaddr相當於lpThreadId

3.在實現上_beginthreadex控制了一個_tiddata的線程資料區塊,裡面存放了線程函數地址、參數的很多屬性,之後再間接調用CreateThread(...);

4._beginthread則參數較少;

3. AfxBeginThread()

MFC提供的介面提供了二種不同類型線程的產生,即工作者線程和使用介面執行緒;可以簡單理解使用介面執行緒包含使用者介面,它有自己的訊息佇列,工作者線程用於計算等;

CWinThread* AfxBeginThread(   AFX_THREADPROC pfnThreadProc,             //線程函數指標,函數原型為UINT _cdecl fnThread(LPVOID pParam);   LPVOID pParam,                            //線程函數參數   int nPriority = THREAD_PRIORITY_NORMAL,   //優先順序,SetThreadPriority   UINT nStackSize = 0,                      //棧大小,單位是bytes,為0時表示按預設大小   DWORD dwCreateFlags = 0,                  //CREATE_SUSPENDED:建立後掛起; 0:建立後立即運行      LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL  //指向SECURITY_ATTRIBUTES結構的指標,為Null時表示預設安全屬性); //建立一個工作者線程CWinThread* AfxBeginThread(   CRuntimeClass* pThreadClass,                //指向介面類指標,繼承自CWinThread   int nPriority = THREAD_PRIORITY_NORMAL,   UINT nStackSize = 0,   DWORD dwCreateFlags = 0,   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ); //建立一個使用介面執行緒

說明:

Header
Afxwin.h

 

 

4. 比較1. _beginthread與_beginthreadex
  1. 在實現上_beginthreadex控制了一個_tiddata的線程資料區塊,裡面存放了線程函數地址、參數的很多屬性,之後再間接調用CreateThread(...);
  2. _beginthread則參數較少,有局限性;
2. AfxBeginThread與mfc的CreateThread
  1. AfxBeginThread一步建立,之後線程立即運行
  2. CWinThread::CreateThread二步建立,但它儲存了線程對象,可以在連續的線程建立與運行完成結束之間再使用(Use CreateThread if you want to reuse the thread object between successive creation and termination of thread executions)

二、線程終結

終結線程的幾個方法:

  1. 等待線程函數運行完成自動結束
  2. ExitThread(),用於結束線程自身
  3. TerminateThread(),所有線程都可以用該方法結束
  4. 父進程關閉,子線程隨之關閉

只建議使用第一種方法結束線程,其它的方式都對應有缺陷

下面給出幾個結束過程中發生事情:

1.資源有序釋放(如作業系統分配資源,用到的C++類析構),返回線程結束代碼,線程核心對象使用計數-1

2.作業系統相關資源釋放;但象C++類並未析構,造成記憶體泄露;這裡如果用_beginthreadex建立線程,而用ExitThread或者_endthread來釋放線程,則線程放在堆上的線程資料區塊_tiddata也未釋放,記憶體泄露;

3.該函數為非同步函數,即通知作業系統終結線程後立即返回,而不管系統是否已經真的結束了線程。同時線程棧也不會釋放

4.用ExitProgerss, TerminateProcess函數關閉進程後,進程會調用TerminateThread來關閉線程,效果如3,線程的棧沒有釋放,申請的對象資源也沒釋放。

1. ExitThread()
VOID ExitThread(  DWORD dwExitCode);

說明:

Header Library
winbase.h coredll.lib

 

 

參數:

dwExitCode: 指定線程的結束代碼。可以通過GetExitCodeThread來查看一個線程的結束代碼

傳回值:無

說明:線上程結束後,會將線程核心對象中的ExitCode由STILL_ACTIVE轉變為傳入結束代碼;與CreateThread對應

2. TerminateThread()
BOOL TerminateThread(  HANDLE hThread,  DWORD dwExitCode);

說明:

Header Library
winbase.h coredll.lib

 

 

參數:

hThread: 要結束的線程控制代碼
dwExitCode: 指定線程的結束代碼。可以通過GetExitCodeThread來查看一個線程的結束代碼

傳回值:0表示失敗,非0表示成功;

3. 判斷線程是否結束
BOOL GetExitCodeThread(  HANDLE hThread,  LPDWORD lpExitCode);
//判斷bool IsThreadExit(HANDLE hThread){    bool bRet = false;    DWORD dwExitCode;    if(GetExitCodeThread(hThread, &dwExitCode))    {        if(dwExitCode != STILL_ACTIVE)            bRet = true;    }    else    {        //error        err = GetLastError();           throw err;    }    return bRet; }

三、注意事項

1.在C++多線程編程中,盡量使用_beginthreadex及_endthreadex,而不是其它介面。

不使用_beginthread原因:

(1)_beginthread函數參數不夠多,某些要求達不到,

不使用_endthread原因:

(1)_endthread函數也是無參的,即線程的結束代碼會被寫入程式碼為0;

(2)該函數在調用ExitThread前,會調用CloseHandle,並傳入新線程的控制代碼。類似下面代碼會有錯誤

DWORD dwExitCode;HANDLE hThread = _beginthreadex(...);GetExitCodeThread(hThread, &dwExitCode);CloseHandle(hThread);

不使用CreateThread函數原因:

(1)標準C/C++運行庫最初並不是為多線程程式而設計的(標準的C執行階段程式庫出現在作業系統對線程支援之前),而CreateThread是作業系統介面,調用它時系統不知道是C/C++來調用的,因此為了保證C/C++程式正常運行,要建立一個資料結構與運行庫的每個線程關聯,_beginthreadex就實現了這樣的功能。換言之,在C/C++中用CreateThread建立線程是極度不安全的。

不使用ExitThread函數原因:

(1)作業系統相關資源釋放;但象C++類並未析構,造成記憶體泄露;這裡如果用_beginthreadex建立線程,而用ExitThread或者_endthread來釋放線程,則線程放在堆上的線程資料區塊_tiddata也未釋放,記憶體泄露;

2.C/C++編程中使用CreateThread會發生什麼

當線程調用一個需要線程資料區塊_tiddata的運行庫函數時,系統會首先通過線程局部儲存(TLS,見下節)來找到線程資料區塊,若為NULL,C/C++運行庫會主調線程分配並初始化一個_tiddata塊並與線程關聯。但若使用C/C++運行庫的signal函數,則整個進程都會終止(因結構化異常處理幀SEH未就緒,RtlUserThreadStart會直接調用ExitProcess來結束進程);此外,若不通過_endthreadex來結束線程,線程資料區塊_tiddata不會釋放,造成記憶體泄露。

參考:

1. <<Windows核心編程(第五版)>>

2. 關於_BEGINTHREADEX、_BEGINTHREAD和CREATETHREAD

3. MFC 多線程及線程同步

4. MSDN

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.