2013-08-20 57 views
1

我想在ThreadPool中几次启动一个简单的委托操作。问题是我需要等待所有的动作完成。如何处理?线程池和委托操作 - WaitHandle.WaitAll()

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)使用了在主类中实现的方法很多...

+0

*但问题是someAction(int)使用了许多在主类中实现的方法* - 你能详细说明一下吗?你的意思是你需要从在线程执行的任务中调用UI线程(在这种情况下,你可能不使用'WaitHandle.WaitAll'来避免阻塞)? – Noseratio

+0

不,我有一个工作类(不是UI线程),应该运行一些异步操作。现在我想等待'x-foreach'循环完成,因为你可以看到'someAction'依赖于'yIndex'。 – Nickon

回答

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回报,当所有任务都做了,并运行它们并行时可能的(就像一个线程池)。

+0

嗯,有趣。 'Parallel.Invoke'是否使用所有机器资源(最高优先级)?因为这是一款应该可以工作大约4-5天的应用程序,并且可以开展一些工作。它应该尽可能多的资源,因为它可以:) – Nickon

+1

一个细节:并行任务只能从.NET Framework 4.0的版本。 –

+1

它使用与应用程序相同的任务计划程序运行,因此提高应用程序的优先级也会提升您的线程。默认情况下,它占用尽可能多的资源。在我的机器上(例如)具有此结构的并行应用程序在所有内核上占用100%(由Windows任务调度程序报告)。如果你想修改行为,你可以实现你自己的调度器,看看这个链接:http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.aspx – Matten

0

我认为你可以使用信号量对象来实现你的目标。试试这个:

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(); 

代码不是确切的,但这个想法应该工作。

+0

然后 - 等到全部完成后再忙? – Matten

1

Matten's advice并使用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排队完成任务。如果你没有遵循这个建议,那么你就会开放出一种微妙的竞争条件,如果一个任务在下一个排队之前完成,就可能发生这种情况。

  2. 您必须对您要 在lambda表达式或匿名委托使用任何循环变量的封闭创建单独的变量。请记住,闭包捕获变量而不是,因此如果您不使用特殊的捕获变量,那么该任务可能不会与您认为的值一起工作。


在这种特殊情况下AddCount居然会抛出一个异常,如果该事件已发出信号。