对于多个线程,请问有人可以比较使用WaitHandle.WaitAllThread.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方法的原因。

09-06 02:53