推薦參考博客:秒殺多線程第六篇 經典線程同步 事件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