9.2.5 使用事件对象完成线程的同步的技巧
1.问题阐述
CEvent类提供了对事件的支持。事件是一个允许一个线程在某种情况发生时,唤醒另外一个线程的同步对象。例如在某些网络应用程序中,一个线程(记为A)负责监听通信端口,另外一个线程(记为B)负责更新用户数据。通过使用CEvent类,线程A可以通知线程B何时更新用户数据。每一个CEvent对象可以有两种状态:有信号状态和无信号状态。线程监视位于其中的CEvent类对象的状态,并在相应的时候采取相应的操作。
2.实现技巧
在MFC中,CEvent 类对象有两种类型:人工事件和自动事件。一个自动CEvent 对象在被至少一个线程释放后会自动返回到无信号状态;而人工事件对象获得信号后,释放可利用线程,但直到调用成员函数ReSetEvent()才将其设置为无信号状态。在创建CEvent 类的对象时,默认创建的是自动事件。CEvent 类的各成员函数的原型和参数说明如下:
CEvent(BOOL bInitiallyOwn=FALSE, BOOL bManualReset=FALSE, LPCTSTR lpszName=NULL, LPSECURITY_ATTRIBUTES lpsaAttribute=NULL); |
bInitiallyOwn:指定事件对象初始化状态,TRUE为有信号,FALSE为无信号。
bManualReset:指定要创建的事件是属于人工事件还是自动事件,TRUE为人工事件,FALSE为自动事件。
将CEvent类对象的状态设置为有信号状态。如果事件是人工事件,则CEvent类对象保持为有信号状态,直到调用成员函数ResetEvent()将其重新设为无信号状态时为止。如果CEvent类对象为自动事件,则在SetEvent()将事件设置为有信号状态后,CEvent类对象由系统自动重置为无信号状态。
如果该函数执行成功,则返回非零值,否则返回零。
BOOL CEvent::ResetEvent();
|
该函数将事件的状态设置为无信号状态,并保持该状态直至SetEvent()被调用时为止。由于自动事件是由系统自动重置的,故自动事件不需要调用该函数。如果该函数执行成功,返回非零值,否则返回零。我们一般通过调用WaitForSingleObject函数来监视事件状态。
3.实例代码
#include #include DWORD WINAPI Fun1Proc( LPVOID lpParameter // 线程数据 ); DWORD WINAPI Fun2Proc( LPVOID lpParameter // 线程数据 ); int tickets=100; HANDLE g_hEvent; void main() { HANDLE hThread1; HANDLE hThread2; hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); CloseHandle(hThread1); CloseHandle(hThread2); //g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets"); if(g_hEvent) { if(ERROR_ALREADY_EXISTS==GetLastError()) { cout<<"only instance can run!"<return; } } SetEvent(g_hEvent); Sleep(4000); CloseHandle(g_hEvent); } DWORD WINAPI Fun1Proc( LPVOID lpParameter // 线程数据 ) { while(TRUE) { WaitForSingleObject(g_hEvent,INFINITE); ResetEvent(g_hEvent); if(tickets>0) { Sleep(1); cout<<"thread1 sell ticket : "<} else break; SetEvent(g_hEvent); } return 0; } DWORD WINAPI Fun2Proc( LPVOID lpParameter // 线程数据 ) { while(TRUE) { WaitForSingleObject(g_hEvent,INFINITE); ResetEvent(g_hEvent); if(tickets>0) { Sleep(1); cout<<"thread2 sell ticket : "<} else break; SetEvent(g_hEvent); } return 0; } |
4.小结
事件对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。有两种不同类型的事件对象:一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
【责任编辑:
夏书 TEL:(010)68476606】