事件对象也可以通过通知操作的方式来保持线程的同步,并且可以实现不同进程中的线程同步操作。
事件对象包含的几个操作原语:
CreateEvent() 创建一个事件对象
OpenEvent() 打开一个事件对象
SetEvent() 设置指定的事件对象为有信号状态
WaitForSingleObject() 等待一个事件
WaitForMultipleObjects() 等待多个事件
WaitForMultipleObjects 函数原型:
WaitForMultipleObjects(
DWORD nCount, // 等待句柄数
CONST HANDLE *lpHandles, //指向句柄数组
BOOL bWaitAll, //是否完全等待标志
DWORD dwMilliseconds //等待时间
)
参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回WAIT_TIMEOUT。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
该函数创建一个Event同步对象,如果CreateEvent调用成功的话,会返回新生成的对象的句柄,否则返回NULL。
参数说明:
lpEventAttributes:一般为NULL
bManualReset:创建的Event是自动复位还是人工复位。如果TRUE,人工复位,一旦该Event被设置为有信号,则它一直会等到ResetEvent()API被调用时才会恢复 为无信号。如果为FALSE,Event被设置为有信号,则当有一个wait到它的Thread时, 该Event就会自动复位,变成无信号。如果想 在每次调用WaitForSingleObject 后让WINDOWS为您自动地把事件地状态恢复为”无信号”状态,必须把该参数设为FALSE,否则,您必须每次调用ResetEvent函数来清除事件 的信号。
bInitialState:初始状态,TRUE,有信号,FALSE无信号
lpName:事件对象的名称。您在OpenEvent函数中可能使用。
注释:
一个Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle()来关闭它,用SetEvent()或PulseEvent()来设置它使其有信号,用ResetEvent()来使其无信号,用WaitForSingleObject()或WaitForMultipleObjects()来等待其变为有信号。
PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event 对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的。
对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于人工复位的Event对象,它释放所有等待的thread。
这里有两个API函数用来修改事件对象的信号状态:SetEvent和ResetEvent。前者把事件对象设为”有信号”状态,而后者正好相反。
在事件对象生成后,必须调用WaitForSingleObject来让线程进入等待状态,该函数的语法如下:
WaitForSingleObject(hObject:DWORD, dwTimeout:DWORD);
hObject -->指向同步对象的指针。事件对象其实是同步对象的一种。
dwTimeout --> 等待同步对象变成”有信号”前等待的时间,以毫秒计。当等待的时间超过该值后无信号同步对象仍处于”无信号”状态,线程不再等待, WaitForSingleObject函数会返回。如果想要线程一直等待,请把该参数设为INFINITE(该值等于0xffffffff)。
事件对象包含的几个操作原语:
CreateEvent() 创建一个事件对象
OpenEvent() 打开一个事件对象
SetEvent() 设置指定的事件对象为有信号状态
WaitForSingleObject() 等待一个事件
WaitForMultipleObjects() 等待多个事件
WaitForMultipleObjects 函数原型:
WaitForMultipleObjects(
DWORD nCount, // 等待句柄数
CONST HANDLE *lpHandles, //指向句柄数组
BOOL bWaitAll, //是否完全等待标志
DWORD dwMilliseconds //等待时间
)
参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回WAIT_TIMEOUT。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
该函数创建一个Event同步对象,如果CreateEvent调用成功的话,会返回新生成的对象的句柄,否则返回NULL。
参数说明:
lpEventAttributes:一般为NULL
bManualReset:创建的Event是自动复位还是人工复位。如果TRUE,人工复位,一旦该Event被设置为有信号,则它一直会等到ResetEvent()API被调用时才会恢复 为无信号。如果为FALSE,Event被设置为有信号,则当有一个wait到它的Thread时, 该Event就会自动复位,变成无信号。如果想 在每次调用WaitForSingleObject 后让WINDOWS为您自动地把事件地状态恢复为”无信号”状态,必须把该参数设为FALSE,否则,您必须每次调用ResetEvent函数来清除事件 的信号。
bInitialState:初始状态,TRUE,有信号,FALSE无信号
lpName:事件对象的名称。您在OpenEvent函数中可能使用。
注释:
一个Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle()来关闭它,用SetEvent()或PulseEvent()来设置它使其有信号,用ResetEvent()来使其无信号,用WaitForSingleObject()或WaitForMultipleObjects()来等待其变为有信号。
PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event 对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的。
对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于人工复位的Event对象,它释放所有等待的thread。
这里有两个API函数用来修改事件对象的信号状态:SetEvent和ResetEvent。前者把事件对象设为”有信号”状态,而后者正好相反。
在事件对象生成后,必须调用WaitForSingleObject来让线程进入等待状态,该函数的语法如下:
WaitForSingleObject(hObject:DWORD, dwTimeout:DWORD);
hObject -->指向同步对象的指针。事件对象其实是同步对象的一种。
dwTimeout --> 等待同步对象变成”有信号”前等待的时间,以毫秒计。当等待的时间超过该值后无信号同步对象仍处于”无信号”状态,线程不再等待, WaitForSingleObject函数会返回。如果想要线程一直等待,请把该参数设为INFINITE(该值等于0xffffffff)。
使用事件对象实现线程同步的代码:
#include <windows.h>
#include <stdio.h>
HANDLE g_hEvent = NULL;
int tickets = 100;
BOOL bThreadStart = TRUE;
DWORD WINAPI FUNCPROC1(LPVOID lpParam);
DWORD WINAPI FUNCPROC2(LPVOID lpParam);
int main()
{
//创建一个自动复位的事件对象
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
SetEvent(g_hEvent); //设置事件对象为有信号状态
HANDLE hThread1 = CreateThread(NULL, 0, FUNCPROC1, NULL, 0, NULL);
HANDLE hThread2 = CreateThread(NULL, 0, FUNCPROC2, NULL, 0, NULL);
getchar();
bThreadStart = FALSE;
if (WAIT_TIMEOUT == WaitForSingleObject(hThread1, 3000))
TerminateThread(hThread1, 0);
CloseHandle(hThread1);
if (WAIT_TIMEOUT == WaitForSingleObject(hThread2, 3000))
TerminateThread(hThread2, 0);
CloseHandle(hThread2);
CloseHandle(g_hEvent);
return 0;
}
DWORD WINAPI FUNCPROC1(LPVOID lpParam)
{
while(bThreadStart)
{
WaitForSingleObject(g_hEvent, INFINITE);
//ResetEvent(g_hEvent) //如果是人工重置的事件对象开启这一句。
//人工重置的事件对象除非显式的调用ResetEvent,将事件对象设置为无信号状态,否则它将始终处于有信号状态。
if (tickets > 0)
{
printf("thread1 sell ticket: %d\n", tickets--);
SetEvent(g_hEvent);
}
else
{
SetEvent(g_hEvent);
break;
}
}
return 0;
}
DWORD WINAPI FUNCPROC2(LPVOID lpParam)
{
while(bThreadStart)
{
WaitForSingleObject(g_hEvent, INFINITE);
//ResetEvent(g_hEvent) //如果是人工重置的事件对象开启这一句。
if (tickets > 0)
{
printf("thread2 sell ticket: %d\n", tickets--);
SetEvent(g_hEvent);
}
else
{
SetEvent(g_hEvent);
break;
}
}
return 0;
}