ThreadPool.QueueUserWorkItem带有任意数量的线程

本文关键字:线程 任意数 QueueUserWorkItem ThreadPool | 更新日期: 2023-09-27 18:16:00

我有一个ConcurrentQueue,它被填充了任意数量的对象,我想在单独的线程中处理这些对象。

我如何等待所有排队的工作项完成?我所看到的示例使用固定的ManualResetEvents数组,然后使用WaitHandle.WaitAll来完成它们。

我需要管理线程数吗?我怎么能让它们一直排队,让ThreadPool处理有多少正在运行呢?队列中将有成千上万个对象。

foreach (object A in myCollection)
{
    ThreadPool.QueueUserWorkItem(A.process());
}
// now wait for all threads to finish

或者我必须在一个列表中跟踪所有的ManualResetEvents,然后等待它们全部报告完成吗?

ThreadPool.QueueUserWorkItem带有任意数量的线程

您不应该简单地将工作项排队,而应该使用任务库(System.Threading.Tasks),它为您提供了更多的功能,包括可替换的调度程序。

使用WaitHandle.WaitAll不是一个非常可扩展的解决方案,因为它有64个句柄限制。

当使用QueueUserWorkItem时,规范的方法是使用CountdownEvent。为每个工作项添加计数,并在工作项完成时发出信号。在下面的示例中,您将注意到我将主线程视为一个工作项,以防止在工作项完成并在队列完成之前发出事件信号时可能发生的非常微妙的竞争条件。

var finished = new CountdownEvent(1);
foreach (object A in myCollection) 
{
  var capture = A; // Required to close over the loop variable 
  finished.AddCount(); // Indicate that there is another work item
  ThreadPool.QueueUserWorkItem(
    (state) =>
    {
      try
      {
        capture.process();
      }
      finally
      {
        finished.Signal(); // Signal work item is complete
      }
    }, null);
} 
finished.Signal(); // Signal queueing is complete
finished.Wait(); // Wait for all work items

如果你决定使用任务并行库,有不同的方法来完成相同的基本事情。

如何(从4.0开始):

Parallel.ForEach(myCollection, a => a.process());