2011-06-30 43 views
15

如果我增加就业机会与QueueUserWorkItem线程池......我怎么把我的计划从直到所有的作业都完成前进?等待QueueUserWorkItem来完成

我知道我可以添加一些逻辑来阻止应用程序运行,直到完成所有作业,但我想知道是否有像Thread.Join()之类的东西,或者是否有任何方法可以检索正在分配作业的每个线程。

+0

您正在使用哪种版本的.NET? –

+0

.NET版本2.0 – PedroC88

回答

38

你可以使用事件同步。就像这样:

private static ManualResetEvent resetEvent = new ManualResetEvent(false); 

public static void Main() 
{ 
    ThreadPool.QueueUserWorkItem(arg => DoWork()); 
    resetEvent.WaitOne(); 
} 

public static void DoWork() 
{ 
    Thread.Sleep(5000); 
    resetEvent.Set(); 
} 

如果您不想嵌入事件设置成你的方法,你可以做这样的事情:

var resetEvent = new ManualResetEvent(false); 
ThreadPool.QueueUserWorkItem(
    arg => 
    { 
     DoWork(); 
     resetEvent.Set(); 
    }); 
resetEvent.WaitOne(); 

多个项目:

var events = new List<ManualResetEvent>(); 

foreach(var job in jobs) 
{ 
    var resetEvent = new ManualResetEvent(false); 
    ThreadPool.QueueUserWorkItem(
     arg => 
     { 
      DoWork(job); 
      resetEvent.Set(); 
     }); 
    events.Add(resetEvent); 
} 
WaitHandle.WaitAll(events.ToArray()); 
+0

这很容易在一段时间排队一个工作努力......但如果我有多个作业,然后我需要一个扣来控制呢?我不是吗?如果不是......这是如何适应多个排队工作? – PedroC88

+2

@ PedroC88 - 更新了答案。 –

+1

这有64个项目的限制。正如其他答案显示,有更多适合的结构来处理多个任务,但是如果您针对的是较早的配置文件,则可以使用单个事件并使用递减的Interlocked.Decrement –

14

的最好的方法是使用CountdownEvent类。这是一个相当完善的模式,并且具有可扩展性。

using (var finished = new CountdownEvent(1)) 
{ 
    foreach (var workitem in workitems) 
    { 
    var capture = workitem; // Used to capture the loop variable in the lambda expression. 
    finished.AddCount(); // Indicate that there is another work item. 
    ThreadPool.QueueUserWorkItem(
     (state) => 
     { 
     try 
     { 
      ProcessWorkItem(capture); 
     } 
     finally 
     { 
      finished.Signal(); // Signal that the work item is complete. 
     } 
     }, null); 
    } 
    finished.Signal(); // Signal that queueing is complete. 
    finished.Wait(); // Wait for all work items to complete. 
} 
+0

这是一个很好的解决方案,不幸的是它只适用于。 NET 4以上。 – newprint

+0

@BrianGideon优秀的答案!但是,我怎么能用'CountdownEvent'过早地取消线程呢?就像我在Windows窗体应用程序中使用它并且用户关闭表单一样,但有线程正在运行。 –

+1

@JohnOdom:这个答案有点过时了。一个更现代的解决方案是使用任务并行库这样的东西。无论如何,列入TPL的是合作取消班。这是应该用来优雅地终止线程的东西。 –

2

您可以使用.NET的Barrier类来实现此目的。

Barrier barrier = new Barrier(3); 
for(int i = 0; i < 2; i++) 
{ 
    ThreadPool.QueueUserWorkItem(
    (state) => 
    { 
     foo(); 
     barrier.SignalAndWait(); 
    }, null); 
} 
barrier.SignalAndWait(); 
+0

DrewR,你能否提供一个回答问题的例子? – Mike

+0

我添加了一个例子 - 注意这是一个.NET 4特性 –