http://hi.baidu.com/netspider_2007/blog/item/c164f63eb6cbd4ca7c1e7130.html
创建或打开一个命名的或无名的事件对象
函数原型:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性
BOOL bManualReset, // 复位方式
BOOL bInitialState, // 初始状态
LPCTSTR lpName // 对象名称
);
参数:
lpEventAttributes:
[输入]一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。
Windows NT/2000:lpEventAttributes的结构中的成员为新的事件指定了一个安全符。如果lpEventAttributes是NULL,事件将获得一个默认的安全符。
bManualReset:
[输入]指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态。
bInitialState:
[输入]指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
lpName:
[输入]指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。
还是上文 多线程与互斥对象 用售票系统为例,介绍事件对象
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(
LPVOID lpParameter // thread data
);
DWORD WINAPI Fun2Proc(
LPVOID lpParameter // thread data
);
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,TRUE,FALSE,NULL);//手动的、无信号状态
Sleep(4000);
}
DWORD WINAPI Fun1Proc(
LPVOID lpParameter // thread data
)
{
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell ticket : "<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);
}
return 0;
}
DWORD WINAPI Fun2Proc(
LPVOID lpParameter // thread data
)
{
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"thread2 sell ticket : "<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);
}
return 0;
}
上面代码执行,没有任何结果,分析原因:
g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//手动的、无信号状态
参数二:bManualReset=TRUE,必须用ResetEvent函数来手工将事件的状态复原到无信号状态
参数三 bInitialState=FALSE 限定改信号在main函数中处于无信号状态;
msdn中 实际要使用setEvent(g_hEvent);
(2)setEvent
setEvent:设置事件的状态为有标记(bInitialState),释放任意等待线程。如果事件是手工的,此事件将保持有标记直到调用ResetEvent。
这种情况下将释放多个线程,如果事件是自动的,此事件将保持有标记,直到一个线程被释放,系统将设置事件的状态为无标记。
如果没有线程在等待,则此事件将保持有标记,直到一个线程被释放。
在上面代码中增加:
g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
setEvent(g_hEvent);
程序运行 但是打印出tickets=0情况,这样肯定不正确,这是为什么呢?
当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。
当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//是人工的
所以两个线程同时执行了,导致了为0情况,所以同步失败了;
(3)ResetEvent
Sets the specified event object to the nonsignaled state.这个针对,手动的而言,需要当调用时候,需要设置为无信号状态:修改线程函数:
DWORD WINAPI Fun1Proc(
LPVOID lpParameter // thread data
)
{
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
ResetEvent(g_hEvent);
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell ticket : "<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);
}
return 0;
}
但是还是没有成功 还是有0出现
(4)对于线程之间同步 不要使用人工重置的对象
使用自动重置的对象,
g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//是自动
int main()
{
HANDLE thread1,thread2;
thread1=CreateThread(NULL,0,Fun1Thread,NULL,0,NULL);
thread2=CreateThread(NULL,0,Fun2Thread,NULL,0,NULL);
CloseHandle(thread1);
CloseHandle(thread2);
g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//自动、无信号
SetEvent(g_hEvent);
Sleep(4000);
CloseHandle(g_hEvent);
return 0;
}
DWORD WINAPI Fun1Thread( LPVOID lpParameter)
{
while(true)
{
WaitForSingleObject(g_hEvent,INFINITE);//signaled state 直到有信号状态,有返回值时候 才执行下面代码
if(tickets>0)
{
Sleep(3);
printf("thread1 tickets=%d\n",tickets--);
}
else
break;
}
return 0;
}
DWORD WINAPI Fun2Thread( LPVOID lpParameter)
{
while(true)
{
WaitForSingleObject(g_hEvent,INFINITE);
if(tickets>0)
{
Sleep(2);
printf("thread2 tickets=%d\n",tickets--);
}
else
break;
}
return 0;
}
结果:打印,tickets=100,其它都没有反应;
当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
线程一中 WaitForSingleObject(g_hEvent,INFINITE);设置为非信号状态;(操作系统分配),这样导致了另外一个线程将不能调度,所以导致上面执行一次;
所以修改一下,让下一个获得请求; 修改如下:
DWORD WINAPI Fun1Proc(
LPVOID lpParameter // thread data
)
{
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell ticket : "<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);//对于自动的设置为有信号状态
}
return 0;