事件对象是指在程序中使用内核对象的有无信号状态实现线程的同步。
1.使用API函数操作事件对象
API函数为CreateEvent;
函数原型为:
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // SD
BOOL bManualReset, // reset type
BOOL bInitialState, // initial state
LPCTSTR lpName // object name);
参数lpEventAttributes是结构体 SECURITY_ATTRIBUTES 的指针,表示新创建的事件对象的安全属性。如果设为NULL, 则使用默认安全属性。
参数bManualReset表示所创建的事件对象是人工重置还是自动重置。如果该参数为true,则表示程序所创建的事件对象为人工重置,如果为false,则表示创建的事件对象为自动重置。
参数bInitialState表示事件对象的初始状态。如果该参数为true,则表示事件对象初始时为有信号状态,否则,为无信状态。
参数npName表示事件对象的名称。如果该参数为NULL,则表示程序创建的是一个匿名的事件对象。
使用WaitForSingleObject主动请求事件对象。
DWORD WaitForSingleObject( HANDLE hHandle, // handle to object
DWORD dwMilliseconds // time-out interval);
参数dwMilliseconds表示该函数将在事件对象上的等待时间,如果为INFINITE,则函数永远等待。
使用SetEvent将指定的事件对象设置为有信号状态。使用ResetEvent将指定的事件对象设置为无信号状态。
BOOL SetEvent( HANDLE hEvent // handle to event);
BOOL ResetEvent( HANDLE hEvent // handle to event);
下面是C语言实现的代码:
#include <stdio.h>
#include <windows.h>
DWORD WINAPI myfun1(LPVOID lpParameter); //声明线程函数
DWORD WINAPI myfun2(LPVOID lpParameter);
HANDLE hevent; //定义全局变量hevent
int a = 0;
int main()
{
HANDLE h1, h2;
hevent = ::CreateEvent(NULL, FALSE, false, NULL);
::SetEvent(hevent);
h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL); //创建线程
printf("线程1开始运行!\r\n");
h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL);
printf("线程2开始运行!\r\n");
::CloseHandle(h1);
::CloseHandle(h2);
::Sleep(10000);
return 0;
}
DWORD WINAPI myfun1(LPVOID lpParameter) //线程函数1
{
while(1)
{
::WaitForSingleObject(hevent, INFINITE); //请求事件对象
::ResetEvent(hevent); //设置事件对象为无信号状态
if(a < 1000)
{
a += 1;
::Sleep(1000);
printf("线程1正在计数%d\r\n", a);
::SetEvent(hevent); //设置事件对象为有信号状态
}
else
{
::SetEvent(hevent);
break;
}
}
return 0;
}
DWORD WINAPI myfun2(LPVOID lpParameter)
{
while(1)
{
::WaitForSingleObject(hevent, INFINITE);
::ResetEvent(hevent);
if(a < 1000)
{
a += 1;
::Sleep(1000);
printf("线程2正在计数%d\r\n", a);
::SetEvent(hevent);
}
else
{
::SetEvent(hevent);
break;
}
}
return 0;
}
结果:
2.使用CEvent类实现线程同步
CEvent函数原型:
CEvent( BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
参数bInitiallyOwn表示事件对象的初始化状态。如果为true,则表示事件对象为有信号状态,否则,为无信号状态,默认为无信号状态。
参数bManualReset表示该事件对象是人工重置还是自动重置。如果该参数为true,则事件对象为人工重置,否则,为自动重置。
参数bpsName表示该事件对象的命名。默认为NULL。
参数bpsaAttribute表示事件对象的安全属性。一般指定为默认安全属性。
下面是C语言实现的代码:
#include <stdio.h>
#include <afxmt.h> //头文件,不是windows.h
DWORD WINAPI myfun1(LPVOID lpParameter); //声明线程函数
DWORD WINAPI myfun2(LPVOID lpParameter);
CEvent event;
int a = 0;
int main()
{
CEvent(false, false, NULL, NULL);
HANDLE h1, h2;
CreateEvent(NULL, FALSE, false, NULL);
event.SetEvent();
h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL); //创建线程
printf("线程1开始运行!\r\n");
h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL);
printf("线程2开始运行!\r\n");
::CloseHandle(h1); //关闭线程句柄对象
::CloseHandle(h2);
::Sleep(10000);
return 0;
}
DWORD WINAPI myfun1(LPVOID lpParameter) //线程函数1
{
while(1)
{
::WaitForSingleObject(event.m_hObject, INFINITE); //请求事件对象
event.ResetEvent();
if(a < 1000) //设置事件对象为无信号状态
{
a += 1;
::Sleep(1000);
printf("线程1正在计数%d\r\n", a);
event.SetEvent(); //设置事件对象为有信号状态
}
else
{
event.SetEvent();
break;
}
}
return 0;
}
DWORD WINAPI myfun2(LPVOID lpParameter)
{
while(1)
{
::WaitForSingleObject(event.m_hObject, INFINITE);
event.ResetEvent();
if(a < 1000)
{
a += 1;
::Sleep(1000);
printf("线程2正在计数%d\r\n", a);
event.SetEvent();
}
else
{
event.SetEvent();
break;
}
}
return 0;
}
结果: