Event对象
最近有使用到event对象,但是对event没有接触过,这里简单的学习一下。先看函数原型:
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes,// 安全属性,NULL为默认的安全属性 BOOL bManualReset,// 复位方式,TRUE为手动复位,FALSE为自动复位 BOOL bInitialState,// 初始状态,TRUE为初始为有信号,FALSE为无信号 LPCTSTR lpName // 对象名称 );
对于复位的方式,提供了手动和自动,自动方式复位是事件被一个等待的线程释放以后,系统会自动将事件设置为无信号状态。手动的意思是你需要设置函数对对象进行复位为无信号,这里有两个函数可以实现:SetEvent()和ResetEvent(),函数原型如下:
BOOL SetEvent(HANDLE hEvent) { }
设置事件对象为有信号,如果成功,就返回非零值,如果失败返回0.
BOOL ResetEvent(HANDLE hEvent) { }
设置事件对象为无信号,如果成功,就返回非零值,如果失败返回0.
一般情况下,事件对象只会在多线程使用,是系统的内核对象。线程本身就是一个死循环,如果我们想要它以某种合适条件退出,Windows会用到event对象,当然还有其他方式。具体是在线程的死循环中不断的调用WaitForSingleObject()或WaitForMultipleObjects()来检查事件是否满足条件,满足就退出线程,不满足就继续运行。这边提到上面的两个函数,介绍一下:
DWORD WINAPI WaitForSingleObject( __in HANDLE hHandle, //可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。 __in DWORD dwMilliseconds //定时时间间隔,单位为毫秒.INFINE无限等待直到hHandle变为有信号。如果为0,函数不会进入等待,直接返回。 );
返回值:
WAIT_ABANDONED 0x00000080:当hHandle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值。
WAIT_OBJECT_0 0x00000000 :核心对象已被激活
WAIT_TIMEOUT 0x00000102:等待超时
WAIT_FAILED 0xFFFFFFFF :出现错误,可通过GetLastError得到错误代码
DWORD WaitForMultipleObjects( DWORD nCount, //事件个数 const HANDLE* lpHandles, //事件数组指针 BOOL bWaitAll, //如果为false,其返回值减去WAIT_OBJECT_0就是lpHandles数组的序号;如果有多个内核事件被触发,将返回序号最小的那个。
如果为true,将等待所有的事件变为有信号才往下执行。
DWORD dwMilliseconds
);
由于多个内核对象被触发时,WaitForMultipleObjects选择其中序号最小的返回。而WaitForMultipleObjects它只会改变使它返回的那个内核对象的状态。这样就产生了序号小的频繁被触发,序号大永远得不到处理,需要用双WaitMultipleObjects来解决:
DWORD WINAPI ThreadProc(LPVOID lpParameter) { DWORD dwRet = 0; int nIndex = 0; while(1) { dwRet = WaitForMultipleObjects(nCount,pHandles,false,INFINITE); switch(dwRet) { case WAIT_TIMEOUT: break; case WAIT_FAILED: return 1; default: { nIndex = dwRet - WAIT_OBJECT_0; ProcessHanlde(nIndex++); //同时检测其他的事件 while(nIndex < nCount) //nCount事件对象总数 { dwRet = WaitForMultipleObjects(nCount - nIndex,&pHandles[nIndex],false,0); switch(dwRet) { case WAIT_TIMEOUT: nIndex = nCount; //退出检测,因为没有被触发的对象了. break; case WAIT_FAILED: return 1; default: { nIndex = nIndex + dwRet - WAIT_OBJECT_0; ProcessHanlde(nIndex++); } break; } } } break; } } return 0; }
这里,写一个简单的程序做示范:
// EventTest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <Windows.h> #include <iostream> using namespace std; HANDLE hEvent = NULL; HANDLE hThread = NULL; DWORD WINAPI threadProc(LPVOID lpThreadParameter) { int count = 0; while(1) { //Sleep(1000); count++; DWORD dw = WaitForSingleObject(hEvent, INFINITE); cout<<"I'm not waiting "<<count<<"s"<<endl; } return 0; } int _tmain(int argc, _TCHAR* argv[]) { int count = 0; hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadProc, NULL, 0, NULL); while(1) { count++; DWORD dw = WaitForSingleObject(hEvent, INFINITE); if(dw != WAIT_OBJECT_0) { cout<<"Faile to wait!"<<endl; return -1; } if(count == 1) { SetEvent(hEvent); } if(count == 500) { ResetEvent(hEvent); } }
cout<<count<<endl; CloseHandle(hEvent); CloseHandle(hThread); system("pause"); return 0; }
上面只是简单地创建一个线程,并使用WaitForSingleObject()等待事件,然后返回到线程函数运行,知道ResetEvent函数设置为无信号以后,线程函数停止运行。