利用事件对象实现线程同步
1) 事件对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。
2) 有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
调用CreateEvent创建事件对象,最后用CloseHandle(hevent)来关闭事件对象,hevent为此事件对象的句柄。
在相应线程的执行程序中,用WaitForSingleObject来实现同步,但要注意:在进行同步时,最好用自动重置的事件,由于当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程,故在线程中执行完相应操作之后,要使用SetEvent来使得事件处于有信号状态。
三种实现同步方法即使用互斥对象、事件对象与关键代码段的比较
1) 互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。
2) 关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。
下面为使用事件对象来模拟简单多线程售卖火车票的例子:
#include <windows.h>
#include <iostream.h>
int tickets=100;
HANDLE hevent;
DWORD WINAPI Proc1Fun(LPVOID lpParameter);
DWORD WINAPI Proc2Fun(LPVOID lpParameter);
main()
{
HANDLE hthread1;
HANDLE hthread2;
hthread1=CreateThread(NULL,0,Proc1Fun,NULL,0,NULL);
hthread2=CreateThread(NULL,0,Proc2Fun,NULL,0,NULL);
hevent=CreateEvent(NULL,FALSE,FALSE,"tickets");
if(hevent)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"only instance can run!"<<endl;
return 1;
}
}
SetEvent(hevent);
CloseHandle(hthread1);
CloseHandle(hthread2);
Sleep(4000);
CloseHandle(hevent);
return 0;
}
DWORD WINAPI Proc1Fun(LPVOID lpParameter)
{
while(TRUE)
{
WaitForSingleObject(hevent,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"Thread1 sells ticket"<<tickets--<<endl;
}
else
break;
SetEvent(hevent);
}
return 0;
}
DWORD WINAPI Proc2Fun(LPVOID lpParameter)
{
while(TRUE)
{
WaitForSingleObject(hevent,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"Thread2 sells ticket"<<tickets--<<endl;
}
else
break;
SetEvent(hevent);
}
return 0;
}