对于多个线程,请问有人可以比较使用WaitHandle.WaitAll
和Thread.Join
的优缺点吗?
最佳答案
WaitHandle.WaitAll
具有64个句柄限制,因此显然是一个很大的限制。另一方面,这是一种仅在一次调用中等待许多信号的便捷方法。 Thread.Join
不需要创建任何其他WaitHandle
实例。并且由于可以在每个线程上单独调用它,因此64个句柄限制不适用。
就个人而言,我从未使用过WaitHandle.WaitAll
。当我想等待多个信号时,我更喜欢更具可扩展性的模式。您可以创建一种递增或递减计数的计数机制,一旦达到特定值,您便会发出一个共享事件的信号。 CountdownEvent
类方便地将所有这些打包到一个类中。
var finished = new CountdownEvent(1);
for (int i = 0; i < NUM_WORK_ITEMS; i++)
{
finished.AddCount();
SpawnAsynchronousOperation(
() =>
{
try
{
// Place logic to run in parallel here.
}
finally
{
finished.Signal();
}
}
}
finished.Signal();
finished.Wait();
更新:
您想要从主线程发出事件信号的原因很微妙。基本上,您希望将主线程视为只是另一个工作项。毕竟,它与其他实际工作项也同时运行。
考虑一下,如果不将主线程视为工作项,会发生什么情况。它将经历
for
循环的一次迭代,并向我们的事件添加一个计数(通过AddCount
),表明我们有一个待处理的工作项对吗?可以说SpawnAsynchronousOperation
完成并让工作项在另一个线程上排队。现在,想象一下主线程在转入循环的下一个迭代之前是否被抢占。执行工作项的线程将获得CPU的应有份额,并开始嗡嗡作响并实际完成工作项。工作项中的Signal
调用运行,并将我们待处理的工作项计数减为零,这会将CountdownEvent
的状态更改为已信号通知。同时,主线程将唤醒并经历循环的所有迭代,并触发Wait
调用,但是由于该事件过早地发出了信号,因此即使仍有待处理的工作项,该事件也会继续进行。同样,当您将主线程视为工作项时,避免这种微妙的竞争状态很容易。这就是为什么将
CountdownEvent
初始化为一个计数,并在Signal
之前调用Wait
方法的原因。