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[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.}
#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[i] = (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也不是那么好用。:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM