C#:等待所有线程完成

我正在写一个我正在编写的代码中的一个通用模式,在那里我需要等待一个组中的所有线程完成,超时。 超时应该是所有线程完成所需的时间,所以简单地为每个线程执行thread.Join(timeout)将不起作用,因为可能的超时是timeout * numThreads。

现在我做一些如下的事情:

var threadFinishEvents = new List<EventWaitHandle>(); foreach (DataObject data in dataList) { // Create local variables for the thread delegate var threadFinish = new EventWaitHandle(false, EventResetMode.ManualReset); threadFinishEvents.Add(threadFinish); var localData = (DataObject) data.Clone(); var thread = new Thread( delegate() { DoThreadStuff(localData); threadFinish.Set(); } ); thread.Start(); } Mutex.WaitAll(threadFinishEvents.ToArray(), timeout); 

然而,这种事情似乎应该有一个更简单的习惯用法。

我仍然认为使用Join更简单。 logging预期的完成时间(如Now + timeout),然后在一个循环中执行

 if(!thread.Join(End-now)) throw new NotFinishedInTime(); 

在.NET 4.0中,我发现System.Threading.Tasks更容易处理。 这里是旋转等待循环,可靠地为我工作。 它阻塞主线程,直到所有的任务完成。 还有Task.WaitAll ,但这并不总是为我工作。

  for (int i = 0; i < N; i++) { tasks[i] = Task.Factory.StartNew(() => { DoThreadStuff(localData); }); } while (tasks.Any(t => !t.IsCompleted)) { } //spin wait 

既然问题碰到了,我会继续发布我的解决scheme。

 using (var finished = new CountdownEvent(1)) { for (DataObject data in dataList) { finished.AddCount(); var localData = (DataObject)data.Clone(); var thread = new Thread( delegate() { try { DoThreadStuff(localData); threadFinish.Set(); } finally { finished.Signal(); } } ); thread.Start(); } finished.Signal(); finished.Wait(YOUR_TIMEOUT); } 

为什么不只是Thread.Join(超时),并从总超时中删除join的时间?

 // pseudo-c#: TimeSpan timeout = timeoutPerThread * threads.Count(); foreach (Thread thread in threads) { DateTime start = DateTime.Now; if (!thread.Join(timeout)) throw new TimeoutException(); timeout -= (DateTime.Now - start); } 

编辑:代码现在更less伪。 不明白你为什么要修改答案-2当你修改+4的答案是完全一样的,只是不太详细。

这不回答问题(没有超时),但我已经做了一个非常简单的扩展方法来等待集合的所有线程:

 using System.Collections.Generic; using System.Threading; namespace Extensions { public static class ThreadExtension { public static void WaitAll(this IEnumerable<Thread> threads) { if(threads!=null) { foreach(Thread thread in threads) { thread.Join(); } } } } } 

那么你只需要打电话:

 List<Thread> threads=new List<Thread>(); //Add your threads to this collection threads.WaitAll(); 

这可能不适合您,但是如果您可以使用.NET的Parallel Extension,那么您可以使用Task而不是原始线程,然后使用Task.WaitAll()等待它们完成。

我想要弄清楚如何做到这一点,但我无法从谷歌得到任何答案。 我知道这是一个古老的线程,但这里是我的解决scheme:

使用以下课程:

 class ThreadWaiter { private int _numThreads = 0; private int _spinTime; public ThreadWaiter(int SpinTime) { this._spinTime = SpinTime; } public void AddThreads(int numThreads) { _numThreads += numThreads; } public void RemoveThread() { if (_numThreads > 0) { _numThreads--; } } public void Wait() { while (_numThreads != 0) { System.Threading.Thread.Sleep(_spinTime); } } } 
  1. 在执行线程之前调用Addthreads(int numThreads)。
  2. 每个完成后调用RemoveThread()。
  3. 在等待所有线程完成的点上使用Wait(),然后继续

我阅读了C#4.0:Herbert Schildt的完整参考书。 作者使用join来给出一个解决scheme:

 class MyThread { public int Count; public Thread Thrd; public MyThread(string name) { Count = 0; Thrd = new Thread(this.Run); Thrd.Name = name; Thrd.Start(); } // Entry point of thread. void Run() { Console.WriteLine(Thrd.Name + " starting."); do { Thread.Sleep(500); Console.WriteLine("In " + Thrd.Name + ", Count is " + Count); Count++; } while (Count < 10); Console.WriteLine(Thrd.Name + " terminating."); } } // Use Join() to wait for threads to end. class JoinThreads { static void Main() { Console.WriteLine("Main thread starting."); // Construct three threads. MyThread mt1 = new MyThread("Child #1"); MyThread mt2 = new MyThread("Child #2"); MyThread mt3 = new MyThread("Child #3"); mt1.Thrd.Join(); Console.WriteLine("Child #1 joined."); mt2.Thrd.Join(); Console.WriteLine("Child #2 joined."); mt3.Thrd.Join(); Console.WriteLine("Child #3 joined."); Console.WriteLine("Main thread ending."); Console.ReadKey(); } } 

Task.Factory.ContinueWhenAll

请参阅: http : //blogs.msdn.com/b/csharpfaq/archive/2010/08/12/blocking-collection-and-the-producer-consumer-problem.aspx

可能的scheme

 var tasks = dataList .Select(data => Task.Factory.StartNew(arg => DoThreadStuff(data), TaskContinuationOptions.LongRunning | TaskContinuationOptions.PreferFairness)) .ToArray(); var timeout = TimeSpan.FromMinutes(1); Task.WaitAll(tasks, timeout); 

假设dataList是项目列表,每个项目需要在一个单独的线程中处理。