我刚刚意识到Windows平台有SignalObjectAndWait
API函数。但已经有SetEvent
和WaitForSingleObject
。您可以将它们结合使用,以实现与SignalObjectAndWait
相同的目标。
根据MSDN,SignalObjectAndWait
比单独调用SetEvent
和WaitForSingleObject
更有效。它还指出:
线程可以使用
SignalObjectAndWait
函数确保工作线程在发送对象信号之前处于等待状态。
我并不完全理解这句话,但似乎效率并不是我们需要SignalObjectAndWait
的唯一原因。任何人都可以提供SetEvent
+ WaitForSingleObject
无法提供SignalObjectAndWait
提供的功能的方案吗?
答案 0 :(得分:2)
MSDN解释的目的是确保在发出事件信号之前线程处于等待状态。如果你调用WaitForSingleObject,那么线程在一个等待状态,但你不能在调用SetEvent之前调用它,因为这将导致SetEvent仅在等待完成后发生 - 如果没有其他任何东西调用SetEvent则没有意义。
答案 1 :(得分:2)
我的理解是,这种单一功能在避免以下情况方面更有效。
与单独的函数调用(例如
SignalObjectAndWait
后跟SetEvent
)相比,WaitForSingleObject
函数提供了一种更有效的方式来发送一个对象,然后然后等待另一个对象。
当你SetEvent
和另一个[特别是更高优先级的线程正在等待此事件,可能会发生线程调度程序控制远离信令线程。当线程接收到控制权时,它所执行的唯一事件是以下WaitForSingleObject
调用,因此浪费了上下文切换这么小的事情。
使用SignalObjectAndWait
你提示内核说“嘿,我会等待另一个事件,所以如果它对你有任何影响,不要过度反复使用上下文切换”。
答案 2 :(得分:1)
如您所知,Microsoft提供了以下示例,说明为什么我们可能需要SignalObjectAndWait,如果我们已经需要单独的SetEvent和WaitForSingleObject(引用Microsoft示例):
线程可以使用SignalObjectAndWait函数来确保工作线程在发送对象信号之前处于等待状态。例如,线程和工作线程可以使用句柄来事件对象来同步它们的工作。该线程执行如下代码:
dwRet = WaitForSingleObject(hEventWorkerDone, INFINITE);
if( WAIT_OBJECT_0 == dwRet)
SetEvent(hEventMoreWorkToDo);
工作线程执行如下代码:
dwRet = SignalObjectAndWait(hEventWorkerDone,
hEventMoreWorkToDo,
INFINITE,
FALSE);
此算法流程存在缺陷,绝不应使用。我们不需要这样一种令人困惑的机制,在这种机制中,线程会相互通知,直到我们处于“竞争状态”。本例中的Microsoft本身创建了Race Condition。工作线程应该只等待一个事件并从列表中获取任务,而生成任务的线程应该只是将任务添加到该列表并发出事件信号。因此,我们只需要一个事件,而不是上面的Microsoft示例中的两个事件。该列表必须受到关键部分的保护。生成任务的线程不应等待工作线程完成任务。如果有任务需要通知某人完成,则任务应自行发送通知。换句话说,任务将在完成时通知线程 - 在完成处理所有任务之前,不是专门等待作业线程的线程。
这样一个有缺陷的设计,就像微软的例子一样,为诸如原子SignalObjectAndWait和原子PulseEvent之类的怪物创造了必要条件 - 最终导致厄运的功能。
这是一个算法如何在问题中实现目标集。只需简单明了的事件,简单的函数SetEvent和WaitForSingleObject即可实现目标 - 无需其他功能。
重要提示:此方案完全基于自动重置事件。您将永远不需要调用ResetEvent。所需的所有函数都是:SetEvent和WaitForMultipleObjects(或WaitForSingleObject)。不需要原子事件操作。
请注意:当我写了一个线程睡觉时,它没有调用"睡眠" API调用 - 它永远不会被需要,只是在"等待"调用WaitForMultipleObjects(或WaitForSingleObject)的结果状态。
如您所知,自动重置事件以及SetEvent和WaitForMultipleObjects函数非常可靠。它们存在于NT 3.1之后。您可能总是构建一个仅依赖于这些简单函数的程序逻辑 - 因此您不需要复杂且不可靠的函数来假设原子操作,如PulseEvent或SignalObjectAndWait。顺便说一下,SignalObjectAndWait只出现在Windows NT 4.0中,而SetEvent和WaitForMultipleObjects确实存在于Win32 - NT 3.1的初始版本中。