事件对象也属于内核对象,允许任意数目的线程对资源拥有同步访问的权限,该对象包含:使用计数、指示人工重置事件/自动重置事件的布尔值、通知状态的布尔值。事件对象包含人工重置事件对象和自动重置事件对象。
人工重置事件对象:当该对象得到通知时,等待该对象的所有线程均变为可调度线程。并且需要调用ResetEvent函数手动设置事件对象为无信号状态。
自动重置事件对象:当该对象得到通知时,等待该对象的所有线程,只有一个变为可调度线程。系统会自动设置该对象为无信号状态,当代码的保护执行完之后要调用SetEvent函数,将其设置为有信号。
创建事件对象的函数原型如下:
CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
bManualReset:指明是人工重置事件对象还是自动重置时间对象。
bInitialState: 指定对象的初始状态,有信号还是无信号。
另外两个函数,SetEvent、ResetEvent为把指定对象设置为有信号状态、无信号状态。关于线程同步的例子如下:
#include <windows.h>
#include <iostream>
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int index = 0;
int tickets = 100;
HANDLE hEvent;
int main()
{
HANDLE hThread1;
HANDLE hThread2;
//创建事件对象
hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
if(hEvent)
{
if (ERROR_ALREADY_EXISTS == GetLastError())
{
std::cout << "only one instance can run!" << std::endl;
return 0;
}
}
//创建线程
hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
Sleep(4000);
std::cout<< "now is the main thread in running\n";
CloseHandle(hEvent);
system("pause");
return 0;
}
//线程1的入口函数
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while (true)
{
WaitForSingleObject(hEvent, INFINITE);
if (tickets>0)
{
Sleep(1);
std::cout << "thread1 sell ticket :" << tickets-- << std::endl;
SetEvent(hEvent);
}
else
{
SetEvent(hEvent);
break;
}
}
return 0;
}
//线程2的入口函数
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while (true)
{
WaitForSingleObject(hEvent,INFINITE);
if (tickets>0)
{
Sleep(1);
std::cout << "thread2 sell ticket :" << tickets-- << std::endl;
SetEvent(hEvent);
}
else
{
SetEvent(hEvent);
break;
}
}
return 0;
}
一般情况下,为了实现线程间的同步,我们不使用人工重置事件对象,而是使用自动重置事件对象,并在初始时,将其设置为有信号状态。