windows核心程式設計-條件變數
當想讓寫入者執行緒和都去這執行緒以獨佔的方式或共享的方式訪問一個資源的時候,可以使用SRWLock。在這些情況下,如果
都去這沒有資料可以讀取,那麼它應該將鎖釋放並等待,直到寫入者執行緒產生了新的資料為止。如果用來接收寫入者執行緒的資料結
構已滿,那麼寫入者同樣應該釋放SRWLock並進入睡眠狀態,直到讀取這執行緒把資料結構清空為止。
我們希望執行緒以原子的方式把鎖釋放並將自己阻塞,直到某一個條件成立為止。要實現這樣的執行緒同步是比較複雜的。windows
通過SleepConditionVariableCS(critical section)或者SleepConditionVariableSRW函式,提供了一種條件變數幫助我們完成這項工作。
當執行緒檢測到相應的條件滿足的時候(比如,由資料供讀取者使用),他會呼叫WakeConditionVariable或WakeAllConditionVariable,
這樣在Sleep*函式中的執行緒就會被喚醒。
#include<windows.h> #include<tchar.h> #include<vector> #include<iostream> #include<process.h> using namespace std; DWORD WINAPI ThreadProduce(PVOID pvParam); DWORD WINAPI ThreadUser1(PVOID pvParam); DWORD WINAPI ThreadUser2(PVOID pvParam); vector<int> ivec; SRWLOCK g_lock; SRWLOCK g_lock2; CONDITION_VARIABLE g_ConditionVar; int _tmain() { InitializeSRWLock(&g_lock); //初始化鎖 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL,0,(unsigned int(_stdcall *)(void*))ThreadProduce,NULL,0,0); HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0, (unsigned int(_stdcall *)(void*))ThreadUser1, NULL, 0, 0); HANDLE hThread3 = (HANDLE)_beginthreadex(NULL, 0, (unsigned int(_stdcall *)(void*))ThreadUser2, NULL, 0, 0); CloseHandle(hThread1); CloseHandle(hThread2); CloseHandle(hThread3); _gettchar(); return 0; } DWORD WINAPI ThreadProduce(PVOID pvParam) { for (int i = 0; i < 10; i++) { AcquireSRWLockExclusive(&g_lock); //獲得SRW鎖 ivec.push_back(i); ReleaseSRWLockExclusive(&g_lock);//釋放SRW鎖 WakeConditionVariable(&g_ConditionVar);//因為每次執行完push_back後,容器裡賣弄就會必定至少有一個元素(生產者) //生產出東西了,這時候阻塞在Sleep*裡面的執行緒就會被喚醒(讀取者sleep執行緒) Sleep(1000);//停一下,讓讀取者先讀 } return 0; } DWORD WINAPI ThreadUser1(PVOID pvParam) { while (1) { AcquireSRWLockExclusive(&g_lock); while (ivec.empty()) { cout << "等待寫入" << endl; //如果容器是空的,也就是沒有內容可以讀,那麼讓執行緒進入睡眠狀態,一直到呼叫WakeConditionAllVariable(&g_ConditionVar) SleepConditionVariableSRW(&g_ConditionVar,&g_lock,INFINITE,CONDITION_VARIABLE_LOCKMODE_SHARED); } cout << "執行緒1:" << ivec.back() << endl; ivec.pop_back(); ReleaseSRWLockExclusive(&g_lock); } return 0; } DWORD WINAPI ThreadUser2(LPVOID pvParam) { while (1) { AcquireSRWLockExclusive(&g_lock); while (ivec.empty()) { cout << "等待寫入2"<<endl; SleepConditionVariableSRW(&g_ConditionVar, &g_lock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED); } cout << "執行緒2:" << ivec.back() << endl; ivec.pop_back(); ReleaseSRWLockExclusive(&g_lock); } return 0; }
分析一下這段程式碼,其實很簡單,具體可以從程式碼註釋中看。之前,寫這段程式碼的時候犯過幾個錯誤:
(1)AcquireSRWLockExclusive() ReleaseSRWLockExclusive() 和 AccquireSRWLockShare() ReleaseSRWLockShare() 兩對函式之間的區別
前者獲得的對保護資源的 獨佔 訪問權 而後者獲得是 保護資源的 共享訪問權,因為雖然 程式碼裡的讀執行緒,在讀取資料的同時,他也pop_back()了容器裡的內容,也就是可以看作是“寫”,因為,我們必須獲得的是 獨佔 訪問權。
(2)WakeConditionVariabel() 和 WakeAllconditionVariable() 的區別:
當呼叫 前者的時候,會使一個在 SleepConditionVariable*函式中等待同一個條件變數被觸發的執行緒得到鎖並返回。當這個執行緒釋放同一個鎖的時候,不會喚醒其他正在等待同一個條件變數的執行緒
當呼叫後者的時候,會使一個或幾個在SleepConditionVariable*函式中等待這個條件變數觸發的執行緒達到對資源的訪問權並返回。
(3)關於 BOOL WINAPI SleepConditionVariableSRW ( __in_out PCONDITION_VARIABLE ,
__in_out PSRWLOCK ,
__in ULONG Flags);
如果引數Flags是 CONDITION_VARIABLE_LOCKMODE_SHARED,那麼在同一時刻可以允許多個讀取者執行緒得到鎖。