多執行緒在C、Win32和MFC下的使用方法
一.前言
執行緒是執行路徑。一個程序至少要有一個執行緒,也可能包含多個執行緒。若程序關閉了,則在程序中所有的執行緒也自動被銷燬。當我們在一個應用程式中建立一個執行緒時,實際上,它是第二個執行緒。在C或C++中,程式的入口函式是main或wmain(Unicode版本)。在windows的運用程式中,程式的入口點是WinMain或wWinMain。當程式啟動時,作業系統建立第一個執行緒。因此,windows是一個多工作業系統。
二.執行緒函式
執行緒函式跟普通函式一樣,它帶有一個long void的指標引數。我們可以傳任何型別的資料給這個void型別的指標資料。一個簡單的執行緒函式如下:
ThreadFunction(LPVOID param)
{
//do something
……
……
//return value;
}
三.執行緒屬性 執行緒的優先順序控制程序中執行緒的優先順序。執行緒屬性如下: ● 最高:THREAD_PRIORITY_HIGHEST ● 高於標準:THREAD_PRIORITY_ABOVE_NORMAL ● 標準:THREAD_PRIORITY_NORMAL ● 低於標準:THREAD_PRIORITY_BELOW_NORMAL ● 空閒:THREAD_PRIORITY_IDEL 我們可以用CreateThread函式設定執行緒優先順序。在Win32 API函式中,我們用GetThreadPriority 和 SetThreadPriority獲取和設定執行緒優先順序;或者我們也可以用CWinThread的函式,在程式碼中,我們可以自由的呼叫它。優先順序函式返回一個BOOL型別的變數。 四.各個平臺下的多執行緒1.C執行時庫的多執行緒 ● _beginthread ● _beginthreadex ● _endthread ● _endthreadex 以上C執行時庫的函式都包含在標頭檔案process.h中。要確保在Microsoft Visual Studio的工程設定是multithreaded DLL。在C執行時庫中,通常是用_beginthread和_beginthreadex函式來建立執行緒。但是,這些執行緒有些不同。_beginthreadex有一些附加的引數,比如安全性和執行緒地址。我們用_beginthread來建立執行緒的話,要用_endthread來結束執行緒。_endthread將自動關閉執行緒的控制代碼。但是,若我們用_endthreadex的話,要用Win32 API的函式CloseHandle來關閉執行緒控制代碼。C執行時庫包含了執行緒本地儲存區(TLS)。我們可以用API或特定的編譯程式碼去使用執行緒本地儲存區。TlsAlloc,TlsFree,TlsGetValue和TlsSetValue通常用來儲存指定的執行緒資料。Microsoft建議,假如你用了C執行時庫的_beginthread函式,你就不要使用像ExitThread或者CreateThread這樣的Win32 API函式。因為,假如你那樣使用的話,可能會導致死鎖。_beginthread在建立執行緒的時候使用多個引數。我們的例子是基於一個簡單控制檯的程式。使用者鍵入執行緒數目建立執行緒,然後我們執行每一個執行緒。
// Secound Thread function void ThreadProc(void *param); // First thread int main() { int n; int i; int val = 0; HANDLE handle; printf("\t Thread Demo\n"); printf("Enter the number of threads : "); scanf("%d",&n); for(i=1;i<=n;i++) { val = i; handle = (HANDLE) _beginthread( ThreadProc,0,&val); // create thread WaitForSingleObject(handle,INFINITE); } return 0; } void ThreadProc(void *param) { int h=*((int*)param); printf("%d Thread is Running!\n",h); _endthread(); }
主執行緒用Win32 API的函式WaitForSingleObject來等待另一個執行緒的完成。
2.MFC的多執行緒
CWinThread是所有執行緒操作的基類。MFC支援兩種型別的執行緒:使用者介面執行緒和工作執行緒。使用者介面執行緒是基於windows訊息。工作程序執行在後臺程序中。CWinThread支援工作執行緒和使用者介面執行緒。但是,這裡只討論工作執行緒。
MFC的類層次結構
CCmdTarget
CWinThread
CWinApp
在以上的類層次結構中,CWinApp應用程式類繼承自CWinThread。因此假如我們建立了一個應用程式類,也同樣建立了執行緒。假如我們建立執行緒的話,它是次執行緒。母類CObject有一些功能像是:支援系列化、執行時間類訊息、支援除錯。派生類CWinThread有同樣的功能。經常用到的一些資料成員和成員函式如下:
資料成員:
● m_bAutoDelete – 設定執行緒是否自動釋放
● m_nThreadID – 當前執行緒的ID
函式成員:
● CreateThread – 啟動執行緒的exec執行● SuspendThread – 掛起執行緒, 增加執行緒掛起數。
● ResumeThread – 恢復執行緒,減少執行緒堆疊數。
● SetThreadPriority – 設定執行緒的優先順序(LOW,BELOW LOW or HIGH)。
● GetThreadPriority – 獲取執行緒的優先順序。
在MFC中,並不是所有的成員函式都是類成員。我們也可以訪問一些全域性函式。這些函式都以Afx開頭。在MFC的執行緒中,AfxBeginThread和AfxEndThread是運用的最廣泛的函式。我們用AfxBeginThread函式建立執行緒。AfxBeginThread語法如下:
CWinThread* AfxBeginThread( AFX_THREADPROC ThreadProc, LPVOID Param, int nPriority = THREAD_PRIORITY_NORMAL,UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );ThreadProc是AfxBeginThread函式的第一個引數,我們在這個引數中使用執行緒函式的名稱,在這個引數中傳入void型別的引數指標,此函式的返回值型別是UINT。AfxBeginThread的其它引數是可選的。預設的優先順序是THREAD_PRIORITY_NORMAL。當想要改變其優先順序時,可以呼叫函式SetThreadPriority。我們同樣也可以獲得優先順序。
AfxEndThread用來終止執行緒,AfxEndThread有一個退出程式碼引數列表。
CwinThread *pThread = AfxBeginThread( ThreadFunction, &data); UINT ThreadFunction(LPVOID param) { DWORD result =0 ; // do somthig AfxEndThread(exitCode); return result; }
3.Win32的多執行緒
Win32的執行緒使用CreateThread函式來建立,CreateThread函式的語法如下:
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
當我們想終止執行緒的時候有下面幾種方法:
(1)使用TerminateThread函式
(2)使用ExitThread函式
(3)使用return
但是Advanced Windows建議我們使用Return方法。TerminateThread或ExitThread不能正確的清除執行緒堆疊。函式GetThreadTimes通常用來獲取執行緒的執行時間。函式GetCurrentThreadID是獲取當前執行緒的ID。Sleep指定執行緒休眠,單位為毫秒。比如,Sleep(1000)將使執行緒休眠1000毫秒。函式SwithToThread的功能是切換到其他執行緒。SuspendThread用來掛起一個執行緒。WaitForSingleObject等待一個指定的執行緒,直到執行緒完全完成它的工作。函式WaitForMultipleObject用來等待多個事件。等待的情形:更改通知、控制檯輸入、事件、工作、互斥、程序、訊號量、執行緒和可等待定時器。
五.執行緒的優點
多執行緒運用程式使用100%的CPU效率。當我們建立一個程序,要需要更多的記憶體空間。多執行緒運用程式跟程序共享一個記憶體空間。每一個執行緒都包含了棧,因此,執行緒比程序佔有的記憶體更少。一個程序可能或可能沒包含多個執行緒,假如你在程序中開啟了多個執行緒,所有的執行緒都共用這個程序的地址空間。