1

我想在ThreadPool. 问题是我需要等待所有操作完成。如何处理?

Action<int> someAction = i => { /* do something */ }
foreach (var yIndex in yRange)
{
    foreach (var xIndex in xRange)
    {
        // ThreadPool.QueueUserWorkItem(??? someAction(yIndex)) - how to start someAction(int) in a thread pool
    }

    WaitHandle.WaitAll(doneEvents); // how to wait for to finish?
}

我知道我可以使用一个新类并创建一个ManualResetEvent回调。但问题是someAction(int)使用了许多在主类中实现的方法......

4

3 回答 3

2

您可以使用任务并行库:

List<Action> actions = new List<Action>();

foreach(var yIndex in yRange)
  foreach(var xIndex in xRange)
    actions.Add(() => someAction(yIndex));

Parallel.Invoke(actions.ToArray());

Parallel.Invoke在所有任务完成后返回并尽可能并行运行它们(就像线程池一样)。

于 2013-08-20T10:59:56.857 回答
1

接受 Matten的建议并使用 TPL 机制。使用 TPL 有许多不同的有效方法可以做到这一点。

如果您好奇这是如何使用老式方法完成的,请继续阅读。一般模式是使用单一的信号机制。为每个操作创建一个WaitHandle根本不可扩展。事实上,该WaitHandle.WaitAll方法无论如何都只接受 64 个句柄。

foreach (var yIndex in yRange)
{
    var yclosure = yIndex;
    var finished = new CountdownEvent(1);
    foreach (var xIndex in xRange)
    {
        var xclosure = xIndex;
        finished.AddCount();
        ThreadPool.QueueUserWorkItem(
          (state) =>
          {
            try
            {
              DoSomething(yclosure, xclosure);
            }
            finally
            {
              finished.Signal();
            }
          }, null);
    }
    finished.Signal();
    finished.Wait();
}

上面的模式CountdownEvent用作信号机制。您也可以使用int计数器和Interlocked方法。人们在使用这种模式时会犯两个常见的错误。

  1. 您应该将主线程视为与其他线程一样的并行任务。这意味着我们需要用 1 个计数初始化信号(代表主线程)。Signal然后我们在循环结束时发出信号表明主线程已完成对任务的排队。如果您未能遵循此建议,那么您可能会出现非常微妙的竞争条件,如果一个任务在下一个任务排队之前完成,则可能会发生这种情况。1

  2. 您必须为要在 lambda 表达式或匿名委托中使用的任何循环变量的闭包创建单独的变量。请记住,闭包捕获的是变量而不是,因此如果您不使用特殊的捕获变量,则任务可能无法使用您认为的值。


1在这种特殊情况下AddCount,如果事件已经发出信号,实际上会引发异常。

于 2013-08-21T15:02:52.857 回答
0

我认为您可以使用 Semaphore 对象来实现您的目标。尝试这个:

int numActions = xRange.Count * yRange.Count; // Set the total number of actions
Semaphore resCount = new Semaphore(0, numActions);
Action<int> someAction = i => { try {/* do something */} finally {resCount.Release(1);} }
// ... the big loop adding actions
resCount.WaitOne();

代码不准确,但这个想法应该可行。

于 2013-08-20T11:07:43.127 回答