C++多線程同步之事件(Event)

原文連接:http://blog.csdn.net/olansefengye1/article/details/53291074

 

1、事件(Event)原理解析

一、線程同步Event,主要用於線程間的等待通知。 
二、內核對象中,事件內核對象是個最基本的對象。 
三、事件包含一個使用計數(與全部內核對象同樣),一個用於指明該事件是個自動重置的事件仍是人工重置的事件的布爾值,另外一個用於指明該事件處於已通知狀態仍是未通知狀態的布爾值。 
四、事件可以通知一個操做已經完成。 
五、有兩種不一樣類型的事件對象。一種是人工重置的事件,另外一種是自動重置的事件。當人工重置的事件獲得通知時,等待該事件的全部線程均變爲可調度線程。當一個自動重置的事件獲得通知時,等待該事件的線程中只有一個線程變爲可調度線程。 
六、當一個線程執行初始化操做,而後通知另外一個線程執行剩餘的操做時,事件使用得最多。 
七、事件初始化爲未通知狀態,而後,當該線程完成它的初始化操做後,它就將事件設置爲已通知狀態。這時,一直在等待該事件的另外一個線程發現該事件已經獲得通知,所以它就變成可調度線程。java

2、Win32平臺源碼

一、相關頭文件及API

函數名 函數說明
CreateEvent Creates or opens a named or unnamed event object.
CreateEventEx Creates or opens a named or unnamed event object and returns a handle to the object.
OpenEvent Opens an existing named event object.
PulseEvent Sets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads.
ResetEvent Sets the specified event object to the nonsignaled state.
SetEvent Sets the specified event object to the signaled state.
/*頭文件*/ #include<windows.h>
HANDLE CreateEvent( LPSECURITY_ATTRIBUTESlpEventAttributes, // 安全屬性 BOOLbManualReset, // 復位方式 BOOLbInitialState, // 初始狀態 LPCTSTRlpName // 對象名稱 );

參數: 
lpEventAttributes[In]: 一個指向SECURITY_ATTRIBUTES結構的指針,肯定返回的句柄是否可被子進程繼承。若是lpEventAttributes是NULL,此句柄不能被繼承。Windows NT/2000:lpEventAttributes的結構中的成員爲新的事件指定了一個安全符。若是lpEventAttributes是NULL,事件將得到一個默認的安全符。 
bManualReset[In]: 指定將事件對象建立成手動復原仍是自動復原。若是是TRUE,那麼必須用ResetEvent函數來手工將事件的狀態復原到無信號狀態。若是設置爲FALSE,當事件被一個等待線程釋放之後,系統將會自動將事件狀態復原爲無信號狀態。 
bInitialState[In]: 指定事件對象的初始狀態。若是爲TRUE,初始狀態爲有信號狀態;不然爲無信號狀態。 
lpName[In]:指定事件的對象的名稱,是一個以0結束的字符串指針。名稱的字符格式限定在MAX_PATH以內。名字是對大小寫敏感的。 若是lpName指定的名字,與一個存在的命名的事件對象的名稱相同,函數將請求EVENT_ALL_ACCESS來訪問存在的對象。這時候,因爲bManualReset和bInitialState參數已經在建立事件的進程中設置,這兩個參數將被忽略。若是lpEventAttributes是參數不是NULL,它將肯定此句柄是否能夠被繼承,可是其安全描述符成員將被忽略。 若是lpName爲NULL,將建立一個無名的事件對象。 若是lpName的和一個存在的信號、互斥、等待計時器、做業或者是文件映射對象名稱相同,函數將會失敗,在GetLastError函數中將返回ERROR_INVALID_HANDLE。形成這種現象的緣由是這些對象共享同一個命名空間。 
返回值:若是函數調用成功,函數返回事件對象的句柄。若是對於命名的對象,在函數調用前已經被建立,函數將返回存在的事件對象的句柄,並且在GetLastError函數中返回ERROR_ALREADY_EXISTS。若是函數失敗,函數返回值爲NULL,若是須要得到詳細的錯誤信息,須要調用GetLastError。linux

HANDLE WINAPI CreateEventEx(
__in_opt LPSECURITY_ATTRIBUTES lpEventAttributes, __in_opt LPCTSTR lpName, __in DWORD dwFlags, __in DWORD dwDesiredAccess );

參數: 
lpEventAttributes[in, optional] :一個指向SECURITY_ATTRIBUTES結構的指針,若是該參數設爲NULL,那麼事件內核對象的句柄不能被子進程繼承. 
lpName[in, optional] :指向事件內核對象的名稱字符串的指針,若是該參數設爲NULL,那麼這個對象被建立爲一個匿名事件內核對象. 
dwFlags[in] :這個參數可被設爲如下一個或多個值:CREATE_ EVENT_ INITIAL_ SET 0x00000002 表示對象初始狀態爲已觸發,不然爲未觸發;CREATE_ EVENT_ MANUAL_RESET 0x00000001 表示這個事件對象必須用ResetEvents函數手動重置,若是不設置這個標誌,系統會在內核對象被釋放後自動重置. 
dwDesiredAccess[in] :訪問權限描述標記。 
返回值:若是函數調用成功,返值是所建立或打開的事件內核對象的句柄.若是調用失敗則返回NULL。ios

HANDLE OpenEvent(
                DWORD dwDesiredAccess,
                BOOL bInheritHandle, LPCTSTR lpName );

參數說明: 
dwDesiredAccess [in]:指定對事件對象的請求訪問權限,若是安全描述符指定的對象不容許要求經過對調用該函數的過程,函數將返回失敗。該參數必須設置爲如下值:EVENT_ALL_ACCESS 指定事件對象全部可能的權限。 
bInheritHandle [in]:指定是否返回的句柄是否繼承 。該參數必須設置爲false。 
lpName[in]:指向一個以null結束的字符串,即將要打開的事件對象的名字。名稱是區分大小寫的。 
返回值:函數執行成功則返回事件對象的句柄;失敗則返回NULL,獲取錯誤信息可使用GetLastError。windows

DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle, __in DWORD dwMilliseconds );

參數: 
hHandle[in]:對象句柄。能夠指定一系列的對象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。 
dwMilliseconds[in]:定時時間間隔,單位爲milliseconds(毫秒).若是指定一個非零值,函數處於等待狀態直到hHandle標記的對象被觸發,或者時間到了。若是dwMilliseconds爲0,對象沒有被觸發信號,函數不會進入一個等待狀態,它老是當即返回。若是dwMilliseconds爲INFINITE,對象被觸發信號後,函數纔會返回。api

DWORD WaitForMultipleObjects(  
  DWORD nCount,             // number of handles in the handle array CONST HANDLE *lpHandles, // pointer to the object-handle array BOOL fWaitAll, // wait flag DWORD dwMilliseconds // time-out interval in milliseconds ); 

參數: 
nCount: 句柄的數量 最大值爲MAXIMUM_WAIT_OBJECTS(64) 
HANDLE: 句柄數組的指針。 HANDLE 類型能夠爲(Event,Mutex,Process,Thread,Semaphore )數組 。 
BOOL bWaitAll: 等待的類型,若是爲TRUE 則等待全部信號量有效在往下執行,FALSE 當有其中一個信號量有效時就向下執行。 
DWORD dwMilliseconds: 超時時間 超時後向執行。 若是爲WSA_INFINITE 永不超時。若是沒有信號量就會在這死等。數組

另外: 
一個Event被建立之後,能夠用OpenEvent()**API來得到它的Handle,用**CloseHandle() 來關閉它,用SetEvent()PulseEvent()來設置它使其有信號,用ResetEvent() 來使其無信號,用WaitForSingleObject()WaitForMultipleObjects()來等待 其變爲有信號。 
PulseEvent()是一個比較有意思的使用方法,正如這個API的名字,它使一個Event 對象的狀態發生一次脈衝變化,從無信號變成有信號再變成無信號,而整個操做是原子的. 對自動復位的Event對象,它僅釋放第一個等到該事件的thread(若是有),而對於 人工復位的Event對象,它釋放全部等待的thread.安全

三、Win32源碼

/************************MyEvent.h******************************/ #ifndef _MY_EVENT_H #define _MY_EVENT_H #include <windows.h> class CMyEvent { public: CMyEvent() { m_hEvent = CreateEvent(NULL /*安全屬性指針*/ , false /*復位方式*/ , true /*初始化狀態*/ , NULL /*事件名稱*/ ); if(NULL == m_hEvent) { return; } } ~CMyEvent() { CloseHandle(m_hEvent); } void Lock() { WaitForSingleObject(m_hEvent,INFINITE); } void UnLock() { SetEvent(m_hEvent); } private: HANDLE m_hEvent; }; class CEventAutoLock { public: CEventAutoLock(CMyEvent* pMyEvent) : m_pMyEvent(pMyEvent) { if(NULL != m_pMyEvent) { m_pMyEvent->Lock(); } } ~CEventAutoLock() { m_pMyEvent->UnLock(); } private: CMyEvent *m_pMyEvent; }; #endif
/****************************main.cpp****************************/ #include <iostream> #include <windows.h> #include "MySemaphore.h" #include "MyMutex.h" #include "MyCriticalSection.h" #include "MyEvent.h" using namespace std; CMySemaphore g_MySemaphore; //信號量 CMyMutex g_MyMutex; //互斥量 CMyCriticalSection g_MyCriticalSection; //臨界區 CMyEvent g_MyEvent; //事件 DWORD WINAPI Fun(LPVOID lpParamter) { string strPrint((const char*)lpParamter); int iRunTime = 0; //執行100次跳出 while(++iRunTime<10) { { CEventAutoLock clock(&g_MyEvent); cout <<"["<< iRunTime <<"]:"<< strPrint.c_str()<<endl; } } return 0; } int main() { //建立五個子線程 string str1 = "A"; string str2 = "B"; string str3 = "C"; string str4 = "D"; string str5 = "E"; HANDLE hThread1 = CreateThread(NULL, 0, Fun, (void*)str1.c_str(), 0, NULL); HANDLE hThread2 = CreateThread(NULL, 0, Fun, (void*)str2.c_str(), 0, NULL); HANDLE hThread3 = CreateThread(NULL, 0, Fun, (void*)str3.c_str(), 0, NULL); HANDLE hThread4 = CreateThread(NULL, 0, Fun, (void*)str4.c_str(), 0, NULL); HANDLE hThread5 = CreateThread(NULL, 0, Fun, (void*)str5.c_str(), 0, NULL); //關閉線程 CloseHandle(hThread1); CloseHandle(hThread2); CloseHandle(hThread3); CloseHandle(hThread4); CloseHandle(hThread5); getchar(); // system("pause"); return 0; }

執行結果: 
這裏寫圖片描述bash

Linux平臺

在Linux平臺下沒有專門的Event(事件)對象,能夠依靠Mutex實現和Event相同的功能。Mutex的使用可參見我前面的博客。app

相關文章
相關標籤/搜索