Window多线程同步(事件)
概述事件是内核对象,用于线程间通信和同步。事件分为无信号状态和有信号状态,无信号状态会阻塞到WaitForSingleObject()函数,直到触发事件(即调用SetEvent)。有信号状态则会忽略事件触发信号,不会阻塞到WaitForSingleObject()函数。 相关APIResetEvent(), WaitForSingleObject(), CreateEvent(
概述
事件是内核对象,用于线程间通信和同步。事件分为无信号状态和有信号状态,无信号状态会阻塞到WaitForSingleObject()函数,直到触发事件(即调用SetEvent)。有信号状态则会忽略事件触发信号,不会阻塞到WaitForSingleObject()函数。
相关API
ResetEvent(), WaitForSingleObject(), CreateEvent(),SetEvent()
函数原型
WINBASEAPI DWORD WINAPI WaitForSingleObject(_In_ HANDLE hHandle,
_In_ DWORD dwMilliseconds);
功能:等待事件被触发
参数:hHandle:事件句柄
dwMilliseconds:等待时间,一般设为INFINITE,意为永久阻塞,直到事件被触发。
如果设为IGNORE则函数立即返回,忽略信号
函数原型
WINBASEAPI _Ret_maybenull_ HANDLE WINAPI CreateEventA(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
_In_ BOOL bManualReset,
_In_ BOOL bInitialState,
_In_opt_ LPCSTR lpName
);
功能:创建事件
参数:lpEventAttributes:表示安全控制,一般直接传入NULL。
bManualReset:判断事件是否手动切换到无信号状态,为true,则需要手动切换,为false,则会自动切换。当调用SetEvent()触发事件时,WaitForSingleObject()就会返回,如果为手动切换,那么必须调用ResetEvent()使事件变成无信号状态,这样下次循环到WaitForSingleObject()函数时就会阻塞,否则WaitForSingleObject()函数会立即返回。果为自动切换,WaitForSingleObject()函数则会自动调用ResetEvent()。
bInitialState:表示事件的初始状态,TRUR表示已触发。
lpName: 事件的名称,NULL表示匿名事件
返回值:事件的句柄
函数原型
ResetEvent()
功能:事件设置成无信号状态
参数:hEvent 事件句柄
函数原型
SetEvent BOOL WINAPI SetEvent(_In_ HANDLE hEvent);
功能:事件触发
参数:hEvent 事件句柄
源码一
HANDLE g_Event = NULL;
unsigned int __stdcall ChildThread(PVOID pM)
{
int i = (int)pM;
char c = (char)(i + 65);
while (true)
{
WaitForSingleObject(g_Event, INFINITE);//等待事件被触发
printf("%c同学抢到电影票,看电影去了\n", c);
}
return 0;
}
unsigned int __stdcall ChildThreadFunc(PVOID pM)
{
while (true)
{
Sleep(1000);
printf("发送事件 \n");
SetEvent(g_Event); //触发事件
}
return 0;
}
int main()
{
HANDLE handles[5] = { 0 };
HANDLE handle = 0;
g_Event = CreateEvent(NULL, FALSE, FALSE, NULL);
for (int i = 0; i < 5; i++)
{
handles[i] = (HANDLE)_beginthreadex(NULL, 0, ChildThread, (void*)i, 0, NULL);
}
handle = (HANDLE)_beginthreadex(NULL, 0, ChildThreadFunc, NULL, 0, NULL);
for (int i = 0; i < 3; i++)
{
WaitForSingleObject(handles[i], -1);
}
WaitForSingleObject(handle, -1);
//释放线程句柄
for (int i = 0; i < 3; i++)
{
CloseHandle(handles[i]);
}
CloseHandle(handle);
//删除事件
CloseHandle(g_Event);
getchar();
return 0;
}
结果
程序中共创建了6个线程,其中一个线程会调用SetEvent()触发事件,其他5个线程阻塞等待,当事件触发后等待的五个线程中的一个会解除阻塞,执行程序。每触发一次,只能使一个线程解除阻塞。由于操作系统实现了一定的公平性,使每个线程都能得到事件触发,从而使结果看起来比较合理。
源码二
HANDLE g_Event = NULL;
unsigned int __stdcall ChildThread(PVOID pM)
{
int i = (int)pM;
char c = (char)(i + 65);
while (true)
{
Sleep(1000);
WaitForSingleObject(g_Event, INFINITE);//等待事件被触发
printf("%c同学抢到电影票,看电影去了\n", c);
ResetEvent(g_Event);
}
return 0;
}
unsigned int __stdcall ChildThreadFunc(PVOID pM)
{
while (true)
{
Sleep(1000);
printf("发送事件 \n");
SetEvent(g_Event); //触发事件
}
return 0;
}
int main()
{
HANDLE handles[5] = { 0 };
HANDLE handle = 0;
g_Event = CreateEvent(NULL, TRUE, FALSE, NULL);
for (int i = 0; i < 5; i++)
{
handles[i] = (HANDLE)_beginthreadex(NULL, 0, ChildThread, (void*)i, 0, NULL);
}
handle = (HANDLE)_beginthreadex(NULL, 0, ChildThreadFunc, NULL, 0, NULL);
for (int i = 0; i < 3; i++)
{
WaitForSingleObject(handles[i], -1);
}
WaitForSingleObject(handle, -1);
//释放线程句柄
for (int i = 0; i < 3; i++)
{
CloseHandle(handles[i]);
}
CloseHandle(handle);
//删除事件
CloseHandle(g_Event);
getchar();
return 0;
}
结果
相对于源码一,源码二只修改了极少的地方,在调用CreateEvent时第二个参数由false改成了true。并在子线程中加了ResetEvent(),其实就是设置成了手动切换事件状态。但结果却大不相同。每触发一次事件,阻塞在子线程上的事件会全部解除阻塞,而不是触发一次,解除一个。但结果不是想象中的那样,出现了事件触发一次,3个子线程在运行,另外两个没有运行?
其实事件只有在等待时才会被接收(执行到WaitForSingleObject),在程序中每个子线程运行都需要一定的时间,当事件触发时,其中三个线程执行完毕阻塞到WaitForSingleObject等待事件,其他两个正在执行,还未正常等待,所以这两个线程就无法接受到事件,而且事件也不会保存,这就导致了以上的结果。
更多推荐
所有评论(0)