您的位置:首页 > 其它

Window多线程同步(事件)

2017-12-25 14:53 106 查看

概述

事件是内核对象,用于线程间通信和同步。事件分为无信号状态和有信号状态,无信号状态会阻塞到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等待事件,其他两个正在执行,还未正常等待,所以这两个线程就无法接受到事件,而且事件也不会保存,这就导致了以上的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: