2

我有一个场景,我将不得不开始大量的线程(可能高达100),然后等待它们完成,然后执行任务(在另一个线程上)。等待所有线程完成的有用模式是什么?

做这类工作的可接受模式是什么?是简单吗?加入?还是现在有更高层次的抽象?

在VS2008中使用.NET 2.0。

+0

你可以使用并行扩展而不是创建自己的线程吗? – Jimmy 2010-02-18 18:28:45

回答

4

在.NET 3.5sp1或.NET 4中,TPL会使这更容易。但是,我将仅针对.NET 2功能进行定制。

有几个选项。使用Thread.Join是完全可以接受的,特别是如果线程是您手动创建的所有线程。这非常简单,可靠并且易于实施。这可能是我的选择。

但是,另一种选择是为总工作量创建计数器,并在计数器达到零时使用重置事件。例如:

class MyClass { 
    int workToComplete; // Total number of elements 
    ManualResetEvent mre; // For waiting 

    void StartThreads() 
    { 
     this.workToComplete = 100; 
     mre = new ManualResetEvent(false); 

     int total = workToComplete; 
     for(int i=0;i<total;++i) 
     { 
      Thread thread = new Thread(new ThreadStart(this.ThreadFunction)); 
      thread.Start(); // Kick off the thread 
     } 

     mre.WaitOne(); // Will block until all work is done 
    } 

    void ThreadFunction() 
    { 
     // Do your work 

     if (Interlocked.Decrement(ref this.workToComplete) == 0) 
      this.mre.Set(); // Allow the main thread to continue here... 
    } 
} 
+2

应该向后运行线程启动循环('for(int i = workToComplete; i> 0; --i)'),以便如果您的ThreadFunction在循环之前完成,Interlocked.Decrement并没有与条件式一起使用。 – Tanzelax 2010-02-18 19:17:15

+0

@坦泽拉斯:好点。我刚刚在那里介绍了一个临时处理(正确)的临时工。很好的接收。 – 2010-02-18 19:29:44

1

你看过ThreadPool吗?看起来像这里 - ThreadPool tutorial,avtor解决了你所要求的相同任务。

+0

waithandle.waitall有64个句柄的限制 – Jimmy 2010-02-18 18:34:15

+0

您可以通过按顺序等待每个元素上的WaitOne来做同样的事情,但是 - 可以使用> 64个等待句柄。 (这就是说,我更喜欢我的方法......) – 2010-02-18 19:17:11

1

什么是运作良好,对我来说是给每个线程的ManagedThreadId存储在字典中,我启动它,然后让每个线程通过其ID后面通过回调方法,当它完成。回调方法从字典中删除id并检查字典的Count属性;当它为零时,你就完成了。确保锁定词典以添加和删除词典。

1

我不确定任何一种标准的线程锁定或同步机制都能真正适用于这么多的线程。但是,这可能是一些情况下,一些基本的消息传递可能是解决问题的理想解决方案。

与其使用Thread.Join会阻塞(并且可能很难管理这么多的线程),您可能会尝试设置另一个线程来聚合来自工作线程的完成消息。当聚合器收到所有预期的消息时,它就完成了。然后,您可以在聚合器和主应用程序线程之间使用一个WaitHandle来表示您的所有工作线程都已完成。

public class WorkerAggregator 
{ 
    public WorkerAggregator(WaitHandle completionEvent) 
    { 
     m_completionEvent = completionEvent; 
     m_workers = new Dictionary<int, Thread>(); 
    } 

    private readonly WaitHandle m_completionEvent; 
    private readonly Dictionary<int, Thread> m_workers; 

    public void StartWorker(Action worker) 
    { 
     var thread = new Thread(d => 
      { 
       worker(); 
       notifyComplete(thread.ManagedThreadID); 
      } 
     ); 

     lock (m_workers) 
     { 
      m_workers.Add(thread.ManagedThreadID, thread); 
     } 

     thread.Start(); 
    } 

    private void notifyComplete(int threadID) 
    { 
     bool done = false; 
     lock (m_workers) 
     { 
      m_workers.Remove(threadID); 
      done = m_workers.Count == 0; 
     } 

     if (done) m_completionEvent.Set(); 
    } 
} 

请注意,我没有测试过上面的代码,所以它可能不是100%正确的。不过,我希望它能说明这个概念足够有用。

+0

您需要启动线程(将其添加到m_workers之后)......它将永远不会像写入的那样运行。 – 2010-02-18 19:30:47

+0

哎呀,我的坏。固定。 – jrista 2010-02-19 21:50:55