天天看點

Win32 多線程的建立方法和基本使用 .

Summary:

總結Win32提供的建立多線程相關的API接口和基本的使用架構。

Ref:

MSDN: http://msdn.microsoft.com/zh-cn/library/y6h8hye8(v=VS.100)

Win32多線程的建立方法主要有:

(1)CreateThread()

(2)_beginthread()&&_beginthreadex()

(3)AfxBeginThread()

(4)CWinThread類

百度百科:http://baike.baidu.com/view/1191444.htm

函數原型:

view plaincopy to clipboardprint?

01.HANDLE CreateThread(

02.  LPSECURITY_ATTRIBUTES lpThreadAttributes,

03.  DWORD dwStackSize,

04.  LPTHREAD_START_ROUTINE lpStartAddress,

05.  LPVOID lpParameter,

06.  DWORD dwCreationFlags,

07.  LPDWORD lpThreadId);

08.}

HANDLE CreateThread(

  LPSECURITY_ATTRIBUTES lpThreadAttributes,

  DWORD dwStackSize,

  LPTHREAD_START_ROUTINE lpStartAddress,

  LPVOID lpParameter,

  DWORD dwCreationFlags,

  LPDWORD lpThreadId);

}

頭檔案:Windows.h

CreateThread是Win32提供的建立線程的最基礎的API,用于在主線程上建立一個線程。傳回一個HANDLE句柄(核心對象)。

參數簡要說明:

lpThreadAttributes:線程屬性,用于設定線程的屬性,NULL表示使用預設的設定。dwStackSize:線程堆棧大小,使用0采用預設設定,windows會根據需要動态增加堆棧大小。lpStartAddress:指向線程函數的指針。lpParameter:向線程函數傳遞的參數。dwCreationFlags:線程标志,CREATE_SUSPENDED表示建立一個挂起的線程,0表示建立後立即激活線程。lpThreadId,先線程的ID(輸出參數)。

建立線程的代碼:

01.#include "stdafx.h"

02.#include

03.

04.DWORD WINAPI ThreadProc(LPVOID lpParam)

05.{

06. printf("sub thread started\n");

07. printf("sub thread finished\n");

08. return 0;

09.}

10.

11.int main(int argc, char* argv[])

12.{

13. DWORD threadID;

14. HANDLE hThread;

15. hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 建立線程

16.

17. return 0;

18.}

#include "stdafx.h"

#include

DWORD WINAPI ThreadProc(LPVOID lpParam)

{

printf("sub thread started\n");

printf("sub thread finished\n");

return 0;

int main(int argc, char* argv[])

DWORD threadID;

HANDLE hThread;

hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 建立線程

如果直接使用上面的代碼,那麼很可能沒有任何輸出,這是由于主線程建立了子線程後主線程繼續向下運作,子線程還沒來得及執行裡面的代碼主線程可能就結束了。這就需要另一個API來進行同步:WaitForSingleObject()。

與之對應的還有WaitForMultipleObjects,用于同步一組核心對象。(參考http://msdn.microsoft.com/zh-cn/site/ms686360擷取所有的同步函數(Synchronization Functions)的使用。

WaitForSingleObject原型:DWORD WINAPI WaitForSingleObject(__in HANDLE hHandle, __in DWORD dwMilliseconds);其中,第一個參數是要等待的核心對象的句柄,第二個參數是設定等待逾時時間,可以設定為INFINITE,表示一直等待直到有信号觸發。

在核心對象使用完畢後,一般需要關閉,使用CloseHandle()函數,參數為核心對象句柄。

是以,以下是一個最基本的使用CreateThread的例子:

07. // TODO: Add your thread code here.

08. printf("sub thread finished\n");

09. return 0;

10.}

11.

12.int main(int argc, char* argv[])

13.{

14. DWORD threadID;

15. HANDLE hThread;

16. hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 建立線程

17.

18. WaitForSingleObject(hThread,INFINITE);

19. CloseHandle(hThread); // 關閉核心對象

20.

21. return 0;

22.}

// TODO: Add your thread code here.

WaitForSingleObject(hThread,INFINITE);

CloseHandle(hThread); // 關閉核心對象

百度百科:http://baike.baidu.com/view/3029167.htm

MSDN:http://msdn.microsoft.com/zh-cn/library/kdzttdcb.aspx

01.uintptr_t _beginthread( // NATIVE CODE

02. void( __cdecl *start_address )( void * ),

03. unsigned stack_size,

04. void *arglist

05.);

uintptr_t _beginthread( // NATIVE CODE

void( __cdecl *start_address )( void * ),

unsigned stack_size,

void *arglist

);

頭檔案:process.h

參數說明:第一個參數是線程函數的指針,第二個參數是堆棧大小,第三個參數是要傳遞給線程函數的參數清單。傳回值也是線程句柄(關于更多說明,參考MSDN)。

同樣,對于_beginthread()的同步,和CreateThread一樣可以使用WaitForSingleObject函數,CloseHandle()關閉核心對象。另外,_beginthread()的線程函數是無傳回值類型的,可以使用_endthread()線上程函數中結束線程。

下面是一個使用_beginthread()的基本的例子:

03.#include

04.

05.void __cdecl ThreadProc(void *para)

06.{

07. printf("sub thread started\n");

08. // TODO: Add your thread code here.

09. printf("sub thread finished\n");

10. _endthread(); // 可以省略,隐含會調用。

11.}

12.

13.int main(int argc, char* argv[])

14.{

15. HANDLE hThread = (HANDLE)_beginthread(ThreadProc, 0, NULL);

17. WaitForSingleObject(hThread,INFINITE);

18. CloseHandle(hThread);

19.}

void __cdecl ThreadProc(void *para)

_endthread(); // 可以省略,隐含會調用。

HANDLE hThread = (HANDLE)_beginthread(ThreadProc, 0, NULL);

CloseHandle(hThread);

}另外,還有一個函數_beginthreadex(),可以簡單的認為_beginthread()為其簡化版,是以更多的時候是使用更簡單的_beginthread()了。

說明:在MSDN中可以看到一句很重要的提示,内容為“For an executable file linked with Libcmt.lib, do not call the Win32 ExitThread API; this prevents the run-time system from reclaiming allocated resources. _endthread and _endthreadex reclaim allocated thread resources and then call ExitThread.”,簡單翻譯就是說,對于連結Libcmt.lib的可執行程式,不要使用Win32的線程退出函數(ExitThread),這會阻止運作時系統回收配置設定的資源,應該使用_endthread,它能回收配置設定的線程資源然後調用ExitThread。這個問題看似沒有提到CreateThread(),但是其實有關,這就是經常看到有些資料上堅決的說到”不要使用CreateThread建立線程,否則會記憶體洩漏“的來源了。

問題引出:CreateThread的記憶體洩漏問題(CreateThread和_beginthread的差別)

Related Topics:http://wenku.baidu.com/view/adede4ec4afe04a1b071dea4.html http://www.cnblogs.com/whiteyun/archive/2011/06/02/2067742.html ....

1. _beginthread也是通過CreateThread來建立線程的,隻是_beginthread對其進行了一些封裝,将相關”資源“通過線程的本地存儲(TLS)傳遞給了線程函數的參數,然後在調用_endthread的時候,會将這些儲存的資源進行釋放。

2. 并不是所有的使用CreateThread的情況都會有記憶體洩漏。看了很多人的文章,隻有http://wenku.baidu.com/view/adede4ec4afe04a1b071dea4.html的分析是最清晰的,我已經轉到http://dl.dbank.com/c03ljl2iud了,可下載下傳檢視(版權歸原作者所有)。

總之,建議是使用_beginthread取代CreateThread來建立線程。

(3)AfxBeginThread():

很顯然,這是MFC中的Afx系列函數,一個在MFC中建立線程的全局函數。由于現在也不怎麼用MFC了,這裡就不多說了。

(4)CWinThread類:

很顯然,是MFC中建立線程的類,同上,不多說了。

歡迎補充!

(1)補充内容:

關于WaitForMultipleObjects在_beginthread無法使用的問題

問題:使用_beginthread建立多個線程,無法使用WaitForMultipleObjects來進行同步。

這個問題可以用下面的例子來測試:

15. DWORD threadID;

16. HANDLE hThread[10];

17. for(int i =0;i<10;i++)

18. hThread[i] = (HANDLE)_beginthread(ThreadProc,0,NULL);

19.

20. WaitForMultipleObjects(10, hThread,TRUE,INFINITE); //無法同步所有線程!

21. for(int i = 0;i<10;i++) {

22. CloseHandle(hThread);

23. }

24.}

HANDLE hThread[10];

for(int i =0;i<10;i++)

hThread[i] = (HANDLE)_beginthread(ThreadProc,0,NULL);

WaitForMultipleObjects(10, hThread,TRUE,INFINITE); //無法同步所有線程!

for(int i = 0;i<10;i++) {

期望的結果是程式能輸出10次的線程建立結束的消息,但是實際運作發現,無法達到這麼多次數。為何?這是因為WaitForMultipleObjects在這裡無法正常工作。原因是:

_endthread()在結束線程的時候,會自動調用CloseHandle關閉核心對象。這就容易解釋了,如果提前關閉了核心對象,WaitForMultipleObjects會傳回錯誤。那麼有沒有專門用于_beginthread建立的線程的同步方法呢,就我目前所知,好象是沒有的!要解決這裡的問題,可以分别調用WaitForSingleObject來同步,當然,使用其他一些變相的方法也是可以的,另外,上面的CloseHandle當然就可以不用再調用了。

總結:看來_beginthread也不是那麼好用。:)

繼續閱讀