推薦參考部落格:秒殺多執行緒第六篇 經典執行緒同步 事件Event
事件是核心物件,多用於執行緒間通訊,可以跨程式同步
事件主要用到三個函式:CreateEvent,OpenEvent,SetEvent,ResetEvent 本文地址
CreateEvent
函式功能:建立事件
函式原型:
HANDLECreateEvent(
LPSECURITY_ATTRIBUTESlpEventAttributes,
BOOLbManualReset,
BOOLbInitialState,
LPCTSTRlpName
);
第一個引數:表示安全控制,一般直接傳入NULL。
第二個引數:確定事件是手動置位還是自動置位,傳入TRUE表示手動置位,傳入FALSE表示自動置位。如果為自動置位,則對該事件呼叫WaitForSingleObject()後會自動呼叫ResetEvent()使事件變成未觸發狀態。打個小小比方,手動置位事件相當於教室門,教室門一旦開啟(被觸發),所以有人都可以進入直到老師去關上教室門(事件變成未觸發)。自動置位事件就相當於醫院裡拍X光的房間門,門開啟後只能進入一個人,這個人進去後會將門關上,其它人不能進入除非門重新被開啟(事件重新被觸發)。
第三個引數:表示事件的初始狀態,傳入TRUR表示已觸發。
第四個引數:表示事件的名稱,傳入NULL表示匿名事件。
返回值:事件的控制程式碼
OpenEvent
函式功能:根據名稱獲得一個事件控制程式碼。
函式原型:
HANDLEOpenEvent(
DWORDdwDesiredAccess,
BOOLbInheritHandle,
LPCTSTRlpName //名稱
);
函式說明:
第一個引數:表示訪問許可權,對事件一般傳入EVENT_ALL_ACCESS。詳細解釋可以檢視MSDN文件。
第二個引數:表示事件控制程式碼繼承性,一般傳入TRUE即可。
第三個引數:表示名稱,不同程式中的各執行緒可以通過名稱來確保它們訪問同一個事件。
返回值:返回事件的控制程式碼
SetEvent
函式功能:觸發事件
函式原型:BOOLSetEvent(HANDLEhEvent);
函式說明:每次觸發後,必有一個或多個處於等待狀態下的執行緒變成可排程狀態。
ResetEvent
函式功能:將事件設為末觸發
函式原型:BOOLResetEvent(HANDLEhEvent);
下面從一個例子說明:假設有三個執行緒都需要使用印表機,我們可以使用互斥量來控制,這樣就可以保證每次只有一個執行緒在使用印表機
使用自動置位,那麼在呼叫WaitForSingleObject()後會自動呼叫ResetEvent()使事件變為未觸發狀態,為了使後面的執行緒能夠繼續列印,需要線上程函式的結尾呼叫SetEvent來觸發事件
#include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; //宣告事件控制程式碼 HANDLE hev; //執行緒繫結的函式返回值和引數是確定的,而且一定要__stdcall unsigned __stdcall threadFun(void *param) { WaitForSingleObject(hev, INFINITE);//等待事件被觸發 for(int i = 0; i < 10; i++) cout<<*(string *)(param)<<" "; cout<<endl; SetEvent(hev);//設定事件為觸發狀態,使後面的執行緒可以列印 return 1; } int main() { //建立一個未被觸發的事件,事件是自動置位的 hev = CreateEvent(NULL, FALSE, FALSE, NULL); SetEvent(hev);// 觸發事件,使執行緒可以列印 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); //千萬別忘了刪除事件 CloseHandle(hev); }
使用手動置位,呼叫WaitForSingleObject()後事件就一直是觸發狀態,執行緒可以任意的列印
#include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; //宣告事件控制程式碼 HANDLE hev; //執行緒繫結的函式返回值和引數是確定的,而且一定要__stdcall unsigned __stdcall threadFun(void *param) { WaitForSingleObject(hev, INFINITE);//等待事件被觸發 for(int i = 0; i < 10; i++) cout<<*(string *)(param)<<" "; cout<<endl; //SetEvent(hev);//設定事件為觸發狀態,使後面的執行緒可以列印 return 1; } int main() { //建立一個未被觸發的事件,事件是手動置位的 hev = CreateEvent(NULL, TRUE, FALSE, NULL); SetEvent(hev);// 觸發事件,使執行緒可以列印 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); //千萬別忘了刪除事件 CloseHandle(hev); }
再通過下面的例子來看看時間有沒有所有權屬性:編寫一個程式,開啟3個執行緒,這3個執行緒的ID分別為A、B、C,每個執行緒將自己的ID在螢幕上列印10遍,要求輸出結果必須按ABC的順序顯示;如:ABCABC….依次遞推
#include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; //宣告3個事件控制程式碼 HANDLE hev1, hev2, hev3; //執行緒繫結的函式返回值和引數是確定的,而且一定要__stdcall unsigned __stdcall threadFunA(void *) { for(int i = 0; i < 10; i++){ WaitForSingleObject(hev1, INFINITE);//等待事件1 cout<<"A"; SetEvent(hev2);//觸發事件2 } return 1; } unsigned __stdcall threadFunB(void *) { for(int i = 0; i < 10; i++){ WaitForSingleObject(hev2, INFINITE);//等待事件2 cout<<"B"; SetEvent(hev3);//觸發事件3 } return 2; } unsigned __stdcall threadFunC(void *) { for(int i = 0; i < 10; i++){ WaitForSingleObject(hev3, INFINITE);//等待事件3 cout<<"C"; SetEvent(hev1);//觸發事件1 } return 3; } int main() { hev1 = CreateEvent(NULL, FALSE, FALSE, NULL); hev2 = CreateEvent(NULL, FALSE, FALSE, NULL); hev3 = CreateEvent(NULL, FALSE, FALSE, NULL); SetEvent(hev1);//觸發事件1,從A開始列印 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); //刪除事件 CloseHandle(hev1); CloseHandle(hev2); CloseHandle(hev3); }
由結果可知事件不具有所有權屬性,即某個執行緒獲取事件後,一定要等待事件再次被觸發。可參考本部落格其他文章中臨界區、互斥量、訊號量的所有權屬性來理解。
【版權宣告】轉載請註明出處:http://www.cnblogs.com/TenosDoIt/p/3601458.html