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

相关文章
相关标签/搜索