推薦參考部落格:秒殺多執行緒第五篇 經典執行緒同步 關鍵段CS
關於臨界區的觀念,一般作業系統書上面都有。
適用範圍:它只能同步一個程式中的執行緒,不能跨程式同步。一般用它來做單個程式內的程式碼快同步,效率比較高
windows中與臨界區有關的結構是 CRITICAL_SECTION,關於該結構體的內部結構可參考here
使用時,主執行緒中要先初始化臨界區,最後要刪除臨界區,具體使用見下面程式碼:
從一個例子來說明:假設有三個執行緒都需要使用印表機,我們可以把列印的程式碼放到臨界區,這樣就可以保證每次只有一個執行緒在使用印表機。
#include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; //定義一個臨界區 CRITICAL_SECTION g_cs; //執行緒繫結的函式返回值和引數是確定的,而且一定要__stdcall unsigned __stdcall threadFun(void *param) { EnterCriticalSection(&g_cs);//進入臨界區,如果有其他執行緒則等待 cout<<*(string *)(param)<<endl; LeaveCriticalSection(&g_cs);//退出臨界區,其他執行緒可以進來了 return 1; } int main() { //初始化臨界區 InitializeCriticalSection(&g_cs); HANDLE hth1, hth2, hth3; string s1 = "first", s2 = "second", s3 = "third"; //建立執行緒 hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL); hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL); hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL); //等待子執行緒結束 WaitForSingleObject(hth1, INFINITE); WaitForSingleObject(hth2, INFINITE); WaitForSingleObject(hth3, INFINITE); //一定要記得關閉執行緒控制程式碼 CloseHandle(hth1); CloseHandle(hth2); CloseHandle(hth3); //刪除臨界區 DeleteCriticalSection(&g_cs); }
再看另外一個問題:編寫一個程式,開啟3個執行緒,這3個執行緒的ID分別為A、B、C,每個執行緒將自己的ID在螢幕上列印10遍,要求輸出結果必須按ABC的順序顯示;如:ABCABC….依次遞推, 仿照文章windows多執行緒同步--訊號量中的程式碼,我們把訊號量替換成臨界區。
#include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; //宣告3個臨界區 CRITICAL_SECTION g_cs1, g_cs2, g_cs3; //執行緒繫結的函式返回值和引數是確定的,而且一定要__stdcall unsigned __stdcall threadFunA(void *) { for(int i = 0; i < 10; i++){ EnterCriticalSection(&g_cs1);//進入臨界區 cout<<"A"; LeaveCriticalSection(&g_cs2);//離開臨界區 } return 1; } unsigned __stdcall threadFunB(void *) { for(int i = 0; i < 10; i++){ EnterCriticalSection(&g_cs2);//進入臨界區 cout<<"B"; LeaveCriticalSection(&g_cs3);//離開臨界區 } return 2; } unsigned __stdcall threadFunC(void *) { for(int i = 0; i < 10; i++){ EnterCriticalSection(&g_cs3);//進入臨界區 cout<<"C"; LeaveCriticalSection(&g_cs1);//離開臨界區 } return 3; } int main() { //初始化臨界區 InitializeCriticalSection(&g_cs1); InitializeCriticalSection(&g_cs2); InitializeCriticalSection(&g_cs3); HANDLE hth1, hth2, hth3; //建立執行緒 hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL); hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL); hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFunC, NULL, 0, NULL); //等待子執行緒結束 WaitForSingleObject(hth1, INFINITE); WaitForSingleObject(hth2, INFINITE); WaitForSingleObject(hth3, INFINITE); //一定要記得關閉執行緒控制程式碼 CloseHandle(hth1); CloseHandle(hth2); CloseHandle(hth3); //刪除臨界區 DeleteCriticalSection(&g_cs1); DeleteCriticalSection(&g_cs2); DeleteCriticalSection(&g_cs3); }
為什麼會這樣呢,因為臨界區有所有權的概念,即某個執行緒進入臨界區後,就擁有該臨界區的所有權,在他離開臨界區之前,他可以無限次的再次進入該臨界區,上例中執行緒A獲得臨界區1的所有權後,線上程C呼叫LeaveCriticalSection(&g_cs1)之前,A是可以無限次的進入臨界區1的。利用訊號量之所以可以實現題目的要求,是因為訊號量沒有所有權的概念,某個執行緒獲得訊號量後,如果訊號量的值為0,那麼他一定要等到訊號量被釋放時,才能再次獲得
關於臨界區的詳細解釋清參考秒殺多執行緒第五篇 經典執行緒同步 關鍵段CS
【版權宣告】轉載請註明出處:http://www.cnblogs.com/TenosDoIt/p/3601308.html