事件:事件Event實際上是個內核對。事件類似於前面的信號量,一個事件有兩種狀態:激發狀態和未激發狀態。也稱有信號狀態和無信號狀態。
事件又分兩種類型:手動重置事件和自動重置事件。手動重置事件被設置為激發狀態后,會喚醒所有等待的線程,而且一直保持為激發狀態,直到程序重新把它設置為未激發狀態。自動重置事件被設置為激發狀態后,會喚醒“一個”等待中的線程,然后自動恢復為未激發狀態。
自動Event可以被抽象為四個操作:
- 創建 CreateEvent(NULL,false,true,NULL);
- 帶觸發 WaitForSingleObject(g_hEvent, INFINITE);
- 重置激活 SetEvent(g_hEvent);
- 銷毀 CloseHandle(g_hEvent);
手動Event可以被抽象為五個操作:
- 創建 CreateEvent(NULL,true,true,NULL);
- 帶觸發 WaitForSingleObject(g_hEvent, INFINITE);
- 重置未激活 ResetEvent(g_hEvent);
- 重置激活 SetEvent(g_hEvent);
- 銷毀 CloseHandle(g_hEvent);
函數解析:
1.
函數功能描述:創建或打開一個命名的或無名的事件對象
函數原型:
HANDLE CreateEvent
(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全屬性
BOOL bManualReset, // 復位方式
BOOL bInitialState, // 初始狀態
LPCTSTR lpName // 對象名稱
);
參數:
lpEventAttributes:
一個指向SECURITY_ATTRIBUTES結構的指針,確定返回的句柄是否可被子進程繼承。如果lpEventAttributes是NULL,此句柄不能被繼承。如果
lpEventAttributes是NULL,事件將獲得一個默認的安全符。
bManualReset:
指定將事件對象創建成手動復原還是自動復原。如果是TRUE,那么必須用ResetEvent函數來手工將事件的狀態復原到無信號狀態。如果設置為FALSE,當事件被一個等待線程釋放以后,系統將會自動將事件狀態復原為無信號狀態。
bInitialState:
指定事件對象的初始狀態。如果為TRUE,初始狀態為有信號狀態;否則為無信號狀態。
lpName:
指定事件的對象的名稱,是一個以0結束的字符串指針。名稱的字符格式限定在MAX_PATH之內。名字是對大小寫敏感的。
注意:
1.如果lpName指定的名字,與一個存在的命名的事件對象的名稱相同,函數將請求EVENT_ALL_ACCESS來訪問存在的對象。這時候,由於bManualReset和bInitialState參數已經在創建事件的進程中設置,這兩個參數將被忽略。如果lpEventAttributes是參數不是NULL,它將確定此句柄是否可以被繼承,但是其安全描述符成員將被忽略。
2.如果lpName為NULL,將創建一個無名的事件對象。
3.如果lpName的和一個存在的信號、互斥、等待計時器、作業或者是文件映射對象名稱相同,函數將會失敗,在GetLastError函數中將返回ERROR_INVALID_HANDLE。造成這種現象的原因是這些對象共享同一個命名空間。
注意點:
手動與自動的區別:
自動重置: SetEvent之后, 事件自動重置為未觸發狀態。
手動重置: SetEvent之后, 需要調用ResetEvent事件才置為未觸發狀態。
區別: 當一個手動重置事件被觸發的時候, 正在等待該事件的所有線程都變為可調度狀態; 當一個自動重置事件被觸發的時候,
只有一個正在等待該事件的線程會變為可調度狀態. 系統並不會保證會調度其中的哪個線程, 剩下的線程將繼續等待. 這樣, 可以在在每個線程函數返回之前調用SetEvent
換句話說:
(1)對於手動置位事件,所有正處於等待狀態下線程都變成可調度狀態。
(2)對於自動置位事件,所有正處於等待狀態下線程只有一個變成可調度狀態。
2.
函數原型:
DWORD WaitForSingleObject
(
HANDLE hHandle,
DWORD dwMilliseconds
);
參數解析:
參數hHandle是一個事件的句柄,第二個參數dwMilliseconds是時間間隔(INFINITE 永久)。如果時間是有信號狀態返回。
WaitForSingleObject函數用來檢測hHandle事件的信號狀態。如果dwMilliseconds為有限事件,則當函數的執行時間超過dwMilliseconds就返回,
但如果參數dwMilliseconds為INFINITE時函數將直到相應時間事件變成有信號狀態才返回,否則就一直等待下去,直到WaitForSingleObject有返回直才執行后面的代碼。
注意:
(1)如果是自動置位事件,那么每一次WaitForSingleObject后,此時狀態就會變成未激發狀態,就要用SetEvent()進行激活。
(2)如果是手動置位事件,每一次WaitForSingleObject后,不會改變原有的狀態。要利用ResetEvent()設置為未激活狀態,再利用SetEvent()進行激活。
3.
SetEvent
函數功能:觸發事件
函數原型:BOOL SetEvent(HANDLEhEvent);
函數說明:每次觸發后,必有一個或多個處於等待狀態下的線程變成可調度狀態。
4.
ResetEvent
函數功能:將事件設為末觸發
函數原型:BOOL ResetEvent(HANDLEhEvent);
源代碼: // Semaphore.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #include <Windows.h> #include <process.h> //線程數 #define g_nThreadNum 5 //信號量 HANDLE g_hEvent; //累加數 int g_nCount = 0; unsigned _stdcall ThreadFunc(void * lParam) { for (int i = 0; i < 100000; ++i) { //等待事件被觸發 ,觸發后變成無信號 WaitForSingleObject(g_hEvent, INFINITE); g_nCount++; //重置事件為激活 SetEvent(g_hEvent); } return 0; } int _tmain(int argc, _TCHAR* argv[]) { //創建事件 g_hEvent=(HANDLE)CreateEvent(NULL,false,true,NULL); //啟動線程 HANDLE pThread[g_nThreadNum]; for (int i = 0; i < g_nThreadNum; ++i) { pThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, 0); if (pThread[i] == 0) { continue; i--; } } //等待線程結束 WaitForMultipleObjects(g_nThreadNum, pThread, TRUE, INFINITE); printf("g_nCount:%d\n", g_nCount); //釋放資源 for (int i = 0; i < g_nThreadNum; ++i) { CloseHandle(pThread[i]); } CloseHandle(g_hEvent); getchar(); return 0; }