对于多线程等待,有人可以比较使用WaitHandle.WaitAll
和Thread.Join
的利弊吗?
答案 0 :(得分:3)
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
之前调用。
答案 1 :(得分:1)
我喜欢@Brian的回答作为两种机制的比较。
如果您使用.Net 4,那么通过Task Parallel Library探索System.Threading.Tasks以实现任务Parellelism是值得的,它允许您在更高抽象级别跨多个线程管理任务。您在此问题中询问的用于管理线程交互的信令是隐藏的或简化的,您可以专注于正确定义每个任务的组成以及如何协调它们。
这似乎是非正式的,但正如微软自己在MSDN文档中所说的那样:
在.NET Framework 4中,任务是 首选的写作API 多线程,异步和 并行代码。
答案 2 :(得分:0)
waitall机制涉及内核模式对象。我不认为连接机制也是如此。如果有机会,我更愿意加入。
从技术上讲,两者并不相同。 IIRC Join
只能在一个线程上运行。 Waitall
可以保存多个内核对象的信号。