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); } } }
- 在执行线程之前调用Addthreads(int numThreads)。
- 每个完成后调用RemoveThread()。
- 在等待所有线程完成的点上使用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是项目列表,每个项目需要在一个单独的线程中处理。