1、线程同步Event,主要用于线程间的等待通知。
2、内核对象中,事件内核对象是个最基本的对象。
3、事件包含一个使用计数(与所有内核对象一样),一个用于指明该事件是个自动重置的事件还是人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。
4、事件能够通知一个操作已经完成。
5、有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
6、当一个线程执行初始化操作,然后通知另一个线程执行剩余的操作时,事件使用得最多。
7、事件初始化为未通知状态,然后,当该线程完成它的初始化操作后,它就将事件设置为已通知状态。这时,一直在等待该事件的另一个线程发现该事件已经得到通知,因此它就变成可调度线程。
函数名 | 函数说明 |
---|---|
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
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。
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。
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
参数说明:
dwDesiredAccess [in]:指定对事件对象的请求访问权限,如果安全描述符指定的对象不允许要求通过对调用该函数的过程,函数将返回失败。该参数必须设置为以下值:EVENT_ALL_ACCESS 指定事件对象所有可能的权限。
bInheritHandle [in]:指定是否返回的句柄是否继承 。该参数必须设置为false。
lpName[in]:指向一个以null结束的字符串,即将要打开的事件对象的名字。名称是区分大小写的。
返回值:函数执行成功则返回事件对象的句柄;失败则返回NULL,获取错误信息可以使用GetLastError。
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,对象被触发信号后,函数才会返回。
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.
/************************MyEvent.h******************************/
#ifndef _MY_EVENT_H
#define _MY_EVENT_H
#include
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;
}
在Linux平台下没有专门的Event(事件)对象,可以依靠Mutex实现和Event相同的功能。Mutex的使用可参见我前面的博客。