产卵多个线程的工作,然后等到所有完成

只是想就multithreading任务的“最佳实践”提出一些build议。

作为一个例子,我们有一个C#应用程序,它在启动时从我们数据库中的各种“types”表中读取数据,并将这些信息存储在我们传递给应用程序的集合中。 这会阻止我们在每次需要这些信息时都敲击数据库。

目前应用程序正在同步从10个表中读取数据。 我真的希望从不同的线程中的每个表中读取应用程序并行运行。 在继续启动应用程序之前,应用程序会等待所有线程完成。

我已经看了BackGroundWorker,但只是想完成上述一些build议。

  1. 该方法听起来是合乎逻辑的,以加快我们的应用程序的启动时间
  2. 我们怎样才能最好地处理所有的线程,牢记每个线程的工作是相互独立的,我们只需要等待所有的线程完成,然后继续。

我期待着一些答案

我的首选是通过一个WaitHandle来处理,并使用Interlocked来避免在计数器上locking:

class Program { static void Main(string[] args) { int numThreads = 10; ManualResetEvent resetEvent = new ManualResetEvent(false); int toProcess = numThreads; // Start workers. for (int i = 0; i < numThreads; i++) { new Thread(delegate() { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // If we're the last thread, signal if (Interlocked.Decrement(ref toProcess) == 0) resetEvent.Set(); }).Start(); } // Wait for workers. resetEvent.WaitOne(); Console.WriteLine("Finished."); } } 

这个效果很好,可以扩展到任意数量的线程处理,而不会引入locking。

我喜欢@里德的解决scheme。 在.NET 4.0中完成相同的另一种方法是使用CountdownEvent 。

 class Program { static void Main(string[] args) { var numThreads = 10; var countdownEvent = new CountdownEvent(numThreads); // Start workers. for (var i = 0; i < numThreads; i++) { new Thread(delegate() { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // Signal the CountdownEvent. countdownEvent.Signal(); }).Start(); } // Wait for workers. countdownEvent.Wait(); Console.WriteLine("Finished."); } } 

如果你有一个STA线程超过64个等待句柄,正如Mark所说的那样。 你可以创build一个列表与你的线程,并等待所有完成在第二个循环。

 //check that all threads have completed. foreach (Thread thread in threadList) { thread.Join(); } 

如果你不在.NET 4.0上,那么你可以使用一个List < ManualResetEvent >,每个线程一个,并等待它们被设置 。 要等待多个线程,你可以考虑使用WaitAll,但要注意64个等待句柄的限制。 如果你需要比这更多的东西,你可以绕过它们,单独等待每一个。

如果你想要更快的启动exprience,你可能不需要等待所有的数据在启动时被读取。 只需显示GUI,任何缺失的信息都可以用某种“正在更新…”图标或类似图标显示。 当信息进入时,只需触发一个事件来更新GUI。 甚至在读入所有表中的所有数据之前,用户可以开始执行许多操作。

如果您感觉冒险,可以使用C#4.0和任务并行库:

 Parallel.ForEach(jobList, curJob => { curJob.Process() }); 

只是为了好玩,@Reed和Monitor一起做了什么。 :P

 class Program { static void Main(string[] args) { int numThreads = 10; int toProcess = numThreads; object syncRoot = new object(); // Start workers. for (int i = 0; i < numThreads; i++) { new Thread(delegate() { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // If we're the last thread, signal if (Interlocked.Decrement(ref toProcess) == 0) { lock (syncRoot) { Monitor.Pulse(syncRoot); } } }).Start(); } // Wait for workers. lock (syncRoot) { if (toProcess > 0) { Monitor.Wait(syncRoot); } } Console.WriteLine("Finished."); } } 

这里有两种等待多个并行操作的模式。 诀窍是,你必须把你的主线程看作是并行操作之一。 否则,在工作线程完成信号和主线程等待信号之间存在细微的竞争条件。

 int threadCount = 1; ManualResetEvent finished = new ManualResetEvent(false); for (int i = 0; i < NUM_WORK_ITEMS; i++) { Interlocked.Increment(ref threadCount); ThreadPool.QueueUserWorkItem(delegate { try { // do work } finally { if (Interlocked.Decrement(ref threadCount) == 0) finished.Set(); } }); } if (Interlocked.Decrement(ref threadCount) == 0) finished.Set(); finished.WaitOne(); 

作为一个个人喜好,我喜欢使用CountdownEvent类来为.NET计数。

 var finished = new CountdownEvent(1); for (int i = 0; i < NUM_WORK_ITEMS; i++) { finished.AddCount(); ThreadPool.QueueUserWorkItem(delegate { try { // do work } finally { finished.Signal(); } }); } finished.Signal(); finished.Wait(); 

上面的例子使用了ThreadPool ,但是你可以把它换成你喜欢的任何线程机制。

TPL的另一种可能性,假定jobs是要处理的项目的集合,或者子线程运行:

 Task.WaitAll(jobs .Select(job => TaskFactory.StartNew(() => /*run job*/)) .ToArray()); 

假设数据库读取器线程完成后立即返回,您可以简单地从启动线程调用所有十个线程上的Thread.Join。

如果您使用的是.NET 3.5或更低版本,则可以使用AsyncResult或BackgroundWorker数组,并计算返回的线程数(请不要忘记使用互锁操作减less计数器)(请参见http://www.albahari.com /线程/作为参考)。

如果您使用.NET 4.0,并行是最简单的方法。

我喜欢使用一个更简单的方法:

  private int ThreadsCount = 100; //initialize threads count private void button1_Click(object sender, EventArgs e) { for (int i = 0; i < ThreadsCount; i++) { Thread t = new Thread(new ThreadStart(myMethod)); t.IsBackground = true; t.Start(); } } private void myMethod() { //everytime a thread finishes executing decrease the threads count ThreadsCount = ThreadsCount - 1; if (ThreadsCount < 1) { //if all threads finished executing do whatever you wanna do here.. MessageBox.Show("Finished Executing all threads!!!"); } } 

发布也许可以帮助其他人,花了相当多的时间寻找像我想出的解决scheme。 所以我采取了一些不同的方法。 我旋转了大量的线程,增加了一个计数器,并在线程启动和停止时递减一个计数器。 然后在主要的方法,我想暂停,等待线程完成我做的。

 while (threadCounter > 0) { Thread.Sleep(500); //Make it pause for half second so that we don't spin the cpu out of control. } 

logging在我的博客上。 http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/