WaitForSingleObject函数在线程同步中最为常见,以至于没有太多的话题还需要展开,
那就写一下容易被忽略的细节,引用书中一个例子
[cpp] view plaincopy
- DWORD dw = WaitForSingleObject(hProcess, 5000)
- switch(dw)
- {
- case WAIT_OBJECT_0:
- break;
- case WAIT_TIMEOUT:
- break;
- case WAIT_FAILED:
- break;
- }
hProcess是进程句柄,进城同属内核对象,在创建进程后进程就被标识为无信号,
直到进程寿终正寝。上面的选择语句给了时间限制,5秒钟,如果在五秒钟的等待过去
后进程仍然苟延残喘,那么就会有返回值WAIT_TIMEOUT,如果在五秒钟内进程就
挂掉了,随之内核对象变成有信号,选择语句就会返回WAIT_OBJECT_0。
并不是每个等待都如此简单,有时候等到的是自己创建的时间内核对象,而事件又分为自动重
置和手动重置,各有利弊,但在WaitForSingleObject中表现的还不够淋漓尽致,
下面用WaitForMultipleObject举例说明,说明时主要针对一些一点和重点展开;
1.
[cpp] view plaincopy
- HANDLE h[3];
- h[0] = hProcess1;
- h[1] = hProcess2;
- h[2] = hProcess3;
- DWORD dw = WaitForMultipleObjects(3, h, FALSE, 5000);
- switch(dw)
- {
- case WAIT_FAILED:
- break;
- case WAIT_TIMEOUT:
- break;
- case WAIT_OBJECT_0 + 0;
- break;
- case WAIT_OBJECT_0 + 1;
- break;
- case WAIT_OBJECT_0 + 2;
- break;
- }
在以上代码中假如创建了三个进程,其中一个进程驾鹤西去,信号从无到有,上面的
等待将被打破,再假如hProces1去了,则switch必定返回WAIT_OBJECT_0+1,
这是正确的,但是,一般我们的程序不是到此处就停止,毕竟三个进程内核对象呢,
不可能死了一个我们的这段代码就没意义了,要是那样的话也根本没必要圈进去三个内核对
象。恩,程序继续执行,再次运行到等待函数时发现那个死了的进程留下的句柄还是有信号
的,于是这时候即使进程3也死了也不会返回最后一个case,而是提前反回了case 2。
结论:要在每个case返回处进行得当处理,以保证再次返回时不受影响。
2.
[cpp] view plaincopy
- HANDLE g_hEvent;
- int WINAPI _tWinMain(...)
- {
- g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- HANDLE hThread1 = _beginthreadex();
- HANDLE hThread2 = _beginthreadex();
- SetEvent(g_hEvent);
- }
- DWORD WINAPI ThreadProc1()
- {
- WaitForSingleObject(g_hEvent, INFINITE);
- // ...
- SetEvent(g_hEvent);
- }
- DWORD WINAPI ThreadProc2()
- {
- WaitForSingleObject(g_hEvent, INFINITE);
- // ...
- SetEvent(g_hEvent);
- }
如上,创建的事件为自动方式,在WaitForSingleObject后会将信号变为无效,必须在访问
省略号中间部分之后重新置为有信号,重置信号后可以继续进行两个线程的代码执行,
这里唯一需要知道是程序没办法给两个线程公平的分配出机会来在有信号的瞬间获得代码处理
机会,很有可能一个线程刚刚处理完一段代码然后将事件从无信号变为有信号,接着就有
等待到了执行代码的机会(WaitForSingleObject成功)。
此外,还应注意,如果我们的程序在使用手动重置事件时,如果允许两个线程同时运行,如一
个来做事件a一个来做事件b,那就一定要保证这两个线程不发生数据访问的冲突,至少不可
以同时写一段内存,一个数据。