Win32 多線程的基本使用

 

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類
(1)CreateThread()
百度百科: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(輸出參數)。
創建線程的代碼:
view plaincopy to clipboardprint?
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); // 創建線程
return 0;
}
如果直接使用上面的代碼,那麼很可能沒有任何輸出,這是由於主線程創建了子線程後主線程繼續向下運行,子線程還沒來得及執行裏面的代碼主線程可能就結束了。這就需要另一個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的例子:
view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
03.
04.DWORD WINAPI ThreadProc(LPVOID lpParam)
05.{
06. printf("sub thread started\n");
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.}
#include "stdafx.h"
#include
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
printf("sub thread started\n");
// TODO: Add your thread code here.
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); // 創建線程
WaitForSingleObject(hThread,INFINITE);
CloseHandle(hThread); // 關閉內核對象
return 0;
}
(2)_beginthread()&&_beginthreadex()
百度百科:http://baike.baidu.com/view/3029167.htm
MSDN:http://msdn.microsoft.com/zh-cn/library/kdzttdcb.aspx
函數原型:
view plaincopy to clipboardprint?
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()的基本的例子:
view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
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);
16.
17. WaitForSingleObject(hThread,INFINITE);
18. CloseHandle(hThread);
19.}
#include "stdafx.h"
#include
#include
void __cdecl ThreadProc(void *para)
{
printf("sub thread started\n");
// TODO: Add your thread code here.
printf("sub thread finished\n");
_endthread(); // 可以省略,隱含會調用。
}
int main(int argc, char* argv[])
{
HANDLE hThread = (HANDLE)_beginthread(ThreadProc, 0, NULL);
WaitForSingleObject(hThread,INFINITE);
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來進行同步。
這個問題可以用下面的例子來測試:
view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
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. DWORD threadID;
16. HANDLE hThread[10];
17. for(int i =0;i<10;i++)
18. hThread = (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.}
#include "stdafx.h"
#include
#include
void __cdecl ThreadProc(void *para)
{
printf("sub thread started\n");
// TODO: Add your thread code here.
printf("sub thread finished\n");
_endthread(); // 可以省略,隱含會調用。
}
int main(int argc, char* argv[])
{
DWORD threadID;
HANDLE hThread[10];
for(int i =0;i<10;i++)
hThread = (HANDLE)_beginthread(ThreadProc,0,NULL);
WaitForMultipleObjects(10, hThread,TRUE,INFINITE); //無法同步所有線程!
for(int i = 0;i<10;i++) {
CloseHandle(hThread);
}
}
期望的結果是程序能輸出10次的線程創建結束的消息,但是實際運行發現,無法達到這麼多次數。爲何?這是因爲WaitForMultipleObjects在這裏無法正常工作。原因是:
_endthread()在結束線程的時候,會自動調用CloseHandle關閉內核對象。這就容易解釋了,如果提前關閉了內核對象,WaitForMultipleObjects會返回錯誤。那麼有沒有專門用於_beginthread創建的線程的同步方法呢,就我目前所知,好象是沒有的!要解決這裏的問題,可以分別調用WaitForSingleObject來同步,當然,使用其他一些變相的方法也是可以的,另外,上面的CloseHandle當然就可以不用再調用了。
總結:看來_beginthread也不是那麼好用。:)

發佈了17 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章