如何确保在特定数量的其他线程运行结束后完全运行线程?

时间:2011-06-13 02:44:40

标签: c# multithreading synchronization semaphore

我在C#中有一个这样的课:

public MyClass
{
   public void Start() { ... }

   public void Method_01() { ... }
   public void Method_02() { ... }
   public void Method_03() { ... }
}

当我调用“Start()”方法时,外部类开始工作,并将创建许多并行线程,这些并行线程在类上面调用“Method_01()”和“Method_02()”形式。在外部类的工作结束后,“Method_03()”将在另一个并行线程中运行。

在创建Method_03()的线程之前创建“Method_01()”或“Method_02()”的线程,但是在“Method_03()”的线程开始之前没有保证结束。我的意思是“Method_01()”或“Method_02()”将丢失他们的CPU转向,“Method_03”将转为CPU并将完全结束。

在“Start()”方法中,我知道应该创建并运行“Method_01”和“Method_02()”的线程总数。问题是我正在寻找一种使用信号量或互斥量的方法来确保“Method_03()”的第一个语句将在运行“Method_01()”或“Method_02()”的所有线程结束后正好运行

5 个答案:

答案 0 :(得分:3)

我想到的三个选项是:

  • 保留一系列Thread个实例,并从Join致电Method_03所有实例。
  • 使用一个CountdownEvent个实例,并从Wait致电Method_03
  • 为每个ManualResetEventMethod_01电话分配一个Method_02,并从WaitHandle.WaitAll拨打Method_03所有这些电话(这不是很可扩展)。< / LI>

我更喜欢使用CountdownEvent,因为它更通用,而且仍然具有超级可扩展性。

public class MyClass
{
  private CountdownEvent m_Finished = new CountdownEvent(0);

  public void Start()
  {
    m_Finished.AddCount(); // Increment to indicate that this thread is active.

    for (int i = 0; i < NUMBER_OF_THREADS; i++)
    {
      m_Finished.AddCount(); // Increment to indicate another active thread.
      new Thread(Method_01).Start();
    }

    for (int i = 0; i < NUMBER_OF_THREADS; i++)
    {
      m_Finished.AddCount(); // Increment to indicate another active thread.
      new Thread(Method_02).Start();
    }

    new Thread(Method_03).Start();

    m_Finished.Signal(); // Signal to indicate that this thread is done.
  }

  private void Method_01()
  {
    try
    {
      // Add your logic here.
    }
    finally
    {
      m_Finished.Signal(); // Signal to indicate that this thread is done.
    }
  }

  private void Method_02()
  {
    try
    {
      // Add your logic here.
    }
    finally
    {
      m_Finished.Signal(); // Signal to indicate that this thread is done.
    }
  }

  private void Method_03()
  {
    m_Finished.Wait(); // Wait for all signals.
    // Add your logic here.
  }
}

答案 1 :(得分:2)

Tasks这似乎是一个完美的工作。下面我假设允许Method01Method02同时运行,没有特定的调用或完成顺序(没有保证,只是输入内存而不进行测试):

int cTaskNumber01 = 3, cTaskNumber02 = 5;
Task tMaster = new Task(() => {
    for (int tI = 0; tI < cTaskNumber01; ++tI)
        new Task(Method01, TaskCreationOptions.AttachedToParent).Start();
    for (int tI = 0; tI < cTaskNumber02; ++tI)
        new Task(Method02, TaskCreationOptions.AttachedToParent).Start();
});
// after master and its children are finished, Method03 is invoked
tMaster.ContinueWith(Method03);
// let it go...
tMaster.Start();

答案 2 :(得分:1)

为什么不使用静态变量static volatile int threadRuns,它将使用数字线程Method_01进行初始化,并运行Method_02。 然后在退出之前修改这两个方法中的每一个以递减threadRuns

...
lock(typeof(MyClass)) {
    --threadRuns;
}
...

然后在Method_03的开头,等到threadRun为0,然后继续:

while(threadRuns != 0)
   Thread.Sleep(10);

我是否正确理解了这个问题?

答案 3 :(得分:1)

你需要做的是为Method_01和Method_02中的每一个创建一个ManualResetEvent(初始化为unset)或其他一些WatHandle,然后让Method_03的线程在句柄集上使用WaitHandle.WaitAll。 / p>

或者,如果你可以引用用于运行Method_01和Method_02的Thread变量,你可以让Method_03的线程使用Thread.Join来等待它们。然而,这假设这些线程在完成Method_01和Method_02的执行时实际终止 - 如果不是,则需要求助于我提到的第一个解决方案。

答案 4 :(得分:1)

在Barrier类中实际上有一个替代品是.Net 4.0中的新选项。这简化了跨多个线程进行信令传递的方式。

您可以执行类似以下代码的操作,但这在同步不同的处理线程时非常有用。

 public class Synchro
    {
        private Barrier _barrier;          

        public void Start(int numThreads)
        {
            _barrier = new Barrier((numThreads * 2)+1);
            for (int i = 0; i < numThreads; i++)
            {
                new Thread(Method1).Start();
                new Thread(Method2).Start(); 
            }
            new Thread(Method3).Start();
        }

        public void Method1()
        {
            //Do some work
            _barrier.SignalAndWait();
        }

        public void Method2()
        {
            //Do some other work.
            _barrier.SignalAndWait();
        }

        public void Method3()
        {
            _barrier.SignalAndWait();               
            //Do some other cleanup work.
        }
    }

我还想建议,因为你的问题陈述是非常抽象的,现在使用新的Parallel或PLINQ功能可以更好地解决使用countdownevent解决的实际问题。如果您实际处理代码中的集合或其他内容,则可能具有以下内容。

 public class Synchro
    {
        public void Start(List<someClass> collection)
        {
            new Thread(()=>Method3(collection));
        }

        public void Method1(someClass)
        {
            //Do some work.               
        }

        public void Method2(someClass)
        {
            //Do some other work.                
        }

        public void Method3(List<someClass> collection)
        {
            //Do your work on each item in Parrallel threads.
            Parallel.ForEach(collection, x => { Method1(x); Method2(x); });
            //Do some work on the total collection like sorting or whatever.                
        }
    }