多線程同步:互斥對象、事件對象、關鍵代碼段
互斥對象、事件對象、關鍵代碼段
一、互斥對象和事件對象屬于內核對象,利用內核對象進行線程同步,速度較慢,但 可以實現在多個進程中各線程間進行同步。
二、關鍵代碼段式工作在用戶方式下,同步速度較快,但在使用關鍵代碼段時,很容易進入死鎖狀態,因為在進入關鍵代碼時無法設定超時值。
MFC下InitializeCriticalSection()和DeleteCriticalSection()可以放在類的構造函數和析構函數中
在編寫程序時首選關鍵代碼段,但需要非常注意死鎖問題!
多線程編程推薦書籍《Windows核心編程》機械工業出版社
三、線程、進程、程序的區別
程序
計算機指令的集合,它以文件的形式存儲在磁盤上
進程
通常被定義為一個正在運行的程序的實例,是一個程序在其自身的地址空間中的一次執行活動
區別:進程是資源申請、調度和獨立運行的單位,因此,它使用系統中的運行資源;而程序不能申請系統資源,不能被系統調度,也不能作為獨立的運行的單位,因此,他不占用系統的運行資源。
進程由兩個部分組成:
1、操作系統用來管理進程的內核對象。內核對象是操作系統內部分配的一個內存塊,內核對象也是系統用來存放關于進程的統計信息的地方。
2、地址空間。它包含所有可執行模塊或DLL模塊的代碼和數據。他還包含動態內存分配的空間。如線程堆棧和堆分配空間。
內核對象:是操作系統內部分配的一個內存塊,它是一種只能被內核訪問的數據結構, 其成員負責維護該對象的各種信息,應用程序無法找到并直接改變它們的內容,只能通過Windows提供的函數對內核對象進行操作。
進程
進程是不活潑的。進程從來不執行任何東西,它只是線程的容器。
若要使進程完成某項操作,它必須擁有一個在它的環境中運行的線程,此線程負責執行包含在進程的地址空間中的代碼。
單個進程可能包含若干個線程,這些線程都“同時”執行進程地址空間中的代碼。
每個進程至少擁有一個線程,來執行進程的地址空間中的代碼。
當創建一個進程時,操作系統會自動創建這個進程的一個線程,稱為主線程。此后,該線程可以創建其他的線程
線程
線程有兩個部分組成:
1。線程的內核對象,操作系統用它來對線程實施管理,內核對象也是系統用來存放線程統計信息的地方。
2。線程堆棧,它用于維護線程在執行代碼時需要的所有參數和局部變量。
當創建線程時,系統創建一個線程內核對象。
該線程內核對象不是線程本身,而是操作系統用來管理線程的較小的數據結構。
可以將線程內核對象視為由關于線程的統計信息組成的一個小型數據結構。
線程總是在某個進程環境中創建。
系統從進程的地址空間中分配內存,供線程的堆棧使用。
新線程運行的進程環境與創建線程的環境相同。
因此,新線程可以訪問進程的內核對象的所有句柄、進程中的所有內存和在這個相同的進程中的所有其他線程的堆棧。這使得單個進程中的多個線程確實能夠非常容易的互相通信。
線程只有一個內核對象和一個堆棧,保留的記錄很少,因此所需要的內存也很少。
因為線程需要的開銷比進程少,因此在編程中經常采用多線程來解決編程問題,而盡量避免創建新的進程。
線程運行
對于單個CPU
操作系統為每一個運行線程安排一定的CPU時間——時間片。
系統通過一種循環的方式為線程提供時間片,線程在自己的時間內運行,因時間片相當短,因此,給用戶的感覺,就好像線程是同時進行的一樣。
如果計算機擁有多個CPU,線程就能真正意義上運行了
注意
我們可以用多進程代替多線程,但是這樣不是明智的,因為
1.每新建一個進程,系統要為之分配4GB的虛擬內存,浪費資源;而多線程共享同一個地址空間,占用資源較少
2.在進程之間發生切換時,要交換整個地址空間;而線程之間的切換只是執行環境的改變,效率較高。
四、多線程具體實現
一、互斥對象
這種辦法不適合在多核CPU的電腦上 。自動重置(只有一個線程可以得到事件對象)
#include
#include
DWORD WINAPI Fun1Proc (LPVOID lpParameter);
DWORD WINAPI Fun2Proc (LPVOID lpParameter);
int index=0;
int tickets=100;
HANDLE hMutex;
void main()
{
HANDLE hThread1,hThread2;
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(hThread1); //關閉句柄沒有終止線程,標示主線程對新線程的引用不感興趣,引用計數減1
CloseHandle(hThread2);
//創建互斥對象
hMutex=CreateMutex(NULL,FALSE,NULL); //FALSE標示該線程(main)不獲得互斥對象的所有權
Sleep(4000);
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter) //當引用計數為0時,終止線程
{
while (TRUE)
{
WaitForSingleObject(hMutex,INFINITE); //INFINITE表示一直等待hMutex有效,不超時退出
if (tickets>0)
{
Sleep(2);
cout<<"thread1 sell tickes :"< } else { break; } ReleaseMutex(hMutex); } return 0; } DWORD WINAPI Fun2Proc (LPVOID lpParameter) { while (TRUE) { WaitForSingleObject(hMutex,INFINITE); if (tickets>0) { cout<<"thread2 sell tickes :"< } else { break; } ReleaseMutex(hMutex); } return 0; } 二、事件對象 這種辦法不適合在多核CPU的電腦上 。自動重置(只有一個線程可以得到事件對象) #include #include DWORD WINAPI Fun1Proc (LPVOID lpParameter); DWORD WINAPI Fun2Proc (LPVOID lpParameter); int tickets=100; HANDLE g_hEvent; void main() { HANDLE hThread1,hThread2; HANDLE g_hEvent1; g_hEvent1=CreateEvent(NULL,FALSE,FALSE,"tickets"); //讓應用程序只能有一個實例 if(g_hEvent1) { if(ERROR_ALREADY_EXISTS==GetLastError()) { cout<<"only one instance can run !"< return; } } g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); //創建自動重置事件內核對象,非信號狀態 hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); CloseHandle(hThread1); //關閉句柄沒有終止線程,標示主線程對新線程的引用不感興趣,引用計數減1 CloseHandle(hThread2); SetEvent(g_hEvent); Sleep(4000); CloseHandle(g_hEvent); } DWORD WINAPI Fun1Proc(LPVOID lpParameter) //當引用計數為0時,終止線程 { while (TRUE) { WaitForSingleObject(g_hEvent,INFINITE); if (tickets>0) { Sleep(2); cout<<"thread1 sell tickes :"< } else { break; } SetEvent(g_hEvent); } return 0; } DWORD WINAPI Fun2Proc (LPVOID lpParameter) { while (TRUE) { WaitForSingleObject(g_hEvent,INFINITE); if (tickets>0) { cout<<"thread2 sell tickes :"< } else { break; } SetEvent(g_hEvent); } return 0; } 三、關鍵代碼段 又稱為臨界區:對資源的獨占權。 初始化臨界區對象函數:InitializeCriticalSection(EnterCriticalSection(LPCRITICAL_SECTION))參數為臨界區對象; 申請臨界區函數:EnterCriticalSection(LPCRITICAL_SECTION)參數為臨界區對象; 釋放臨界區函數:LeaveCriticalSection(LPCRITICAL_SECTION)參數為臨界區對象; 釋放臨界區資源:DeleteCriticalSection(&g_cs); 使用關鍵代碼段的線程同步代碼: #include #include #include DWORD WINAPI Fun1Proc (LPVOID lpParameter); DWORD WINAPI Fun2Proc (LPVOID lpParameter); int tickets=100; CRITICAL_SECTION g_cs; void main() { HANDLE hThread1,hThread2; HANDLE g_hEvent1; g_hEvent1=CreateEvent(NULL,FALSE,FALSE,"tickets"); //讓應用程序只能有一個實例 if(g_hEvent1) { if(ERROR_ALREADY_EXISTS==GetLastError()) { cout<<"only one instance can run !"< return; } } InitializeCriticalSection(&g_cs); //必須在創建線程之前初始化臨界區對象,否則會出錯 // g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); //創建自動重置事件內核對象,非信號狀態 hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); CloseHandle(hThread1); //關閉句柄沒有終止線程,標示主線程對新線程的引用不感興趣,引用計數減1 CloseHandle(hThread2); // SetEvent(g_hEvent); // getchar(); Sleep(4000); DeleteCriticalSection(&g_cs); getchar(); // CloseHandle(g_hEvent); } DWORD WINAPI Fun1Proc(LPVOID lpParameter) //當引用計數為0時,終止線程 { while (TRUE) { EnterCriticalSection(&g_cs); if (tickets>0) { Sleep(2); cout<<"thread1 sell tickes :"< LeaveCriticalSection(&g_cs); } else { LeaveCriticalSection(&g_cs); break; } } return 0; } DWORD WINAPI Fun2Proc (LPVOID lpParameter) { while (TRUE) { EnterCriticalSection(&g_cs); if (tickets>0) { cout<<"thread2 sell tickes :"< LeaveCriticalSection(&g_cs); } else { LeaveCriticalSection(&g_cs); break; } } return 0; } 三、線程死鎖問題: #include #include #include DWORD WINAPI Fun1Proc (LPVOID lpParameter); DWORD WINAPI Fun2Proc (LPVOID lpParameter); int tickets=100; CRITICAL_SECTION g_csA; CRITICAL_SECTION g_csB; void main() { HANDLE hThread1,hThread2; HANDLE g_hEvent1; g_hEvent1=CreateEvent(NULL,FALSE,FALSE,"tickets"); //讓應用程序只能有一個實例 if(g_hEvent1) { if(ERROR_ALREADY_EXISTS==GetLastError()) { cout<<"only one instance can run !"< return; } } InitializeCriticalSection(&g_csA); InitializeCriticalSection(&g_csB); // g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); //創建自動重置事件內核對象,非信號狀態 hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); CloseHandle(hThread1); //關閉句柄沒有終止線程,標示主線程對新線程的引用不感興趣,引用計數減1 CloseHandle(hThread2); // SetEvent(g_hEvent); // getchar(); Sleep(4000); DeleteCriticalSection(&g_csA); DeleteCriticalSection(&g_csB); // CloseHandle(g_hEvent); } DWORD WINAPI Fun1Proc(LPVOID lpParameter) //當引用計數為0時,終止線程 { while (TRUE) { EnterCriticalSection(&g_csA); // Sleep(1); EnterCriticalSection(&g_csB); if (tickets>0) { Sleep(2); cout<<"thread1 sell tickes :"< LeaveCriticalSection(&g_csB); LeaveCriticalSection(&g_csA); } else { LeaveCriticalSection(&g_csB); LeaveCriticalSection(&g_csA); break; } } return 0; } DWORD WINAPI Fun2Proc (LPVOID lpParameter) { while (TRUE) { EnterCriticalSection(&g_csB); // Sleep(1); EnterCriticalSection(&g_csA); if (tickets>0) { cout<<"thread2 sell tickes :"< LeaveCriticalSection(&g_csA); LeaveCriticalSection(&g_csB); } else { LeaveCriticalSection(&g_csA); LeaveCriticalSection(&g_csB); break; } } return 0; } 任務調度 多線程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。