如何确保一定数量的其他线程运行结束后恰好运行线程?

| 我在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_01()\”或\“ Method_02()\”的线程是在创建Method_03()的线程之前创建的,但是没有保证要在\“ Method_03()\”的线程开始之前结束。我的意思是\“ Method_01()\”或\“ Method_02()\”将失去其CPU转动,而\“ Method_03 \”将使CPU转动并完全结束。 在\“ Start()\”方法中,我知道应该创建并运行\“ Method_01 \”和\“ Method_02()\”的线程总数。问题是,我正在寻找一种使用信号量或互斥量的方法,以确保\“ Method_03()\”的第一条语句将在所有正在运行\“ Method_01()\”的线程结束后准确运行,或者\“ Method_02()\”。     
已邀请:
        想到的三个选项是: 保留一个数组
Thread
实例,并从
Method_03
对其调用
Join
。 使用单个
CountdownEvent
实例,然后从
Method_03
调用
Wait
。 为每个
Method_01
Method_02
呼叫分配一个
ManualResetEvent
,然后从
Method_03
对其全部调用
WaitHandle.WaitAll
(这不是很容易扩展)。 我更喜欢使用
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.
  }
}
    
        这对于ѭ14来说似乎是完美的工作。下面我假设允许
Method01
Method02
并发运行,而没有特定的调用或完成顺序(没有保证,只是在没有测试的情况下键入内存即可):
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();
    
        听起来您需要做的是为Method_01和Method_02中的每一个创建一个ManualResetEvent(初始化为未设置)或其他一些WatHandle,然后让Method_03的线程在该句柄集上使用WaitHandle.WaitAll。 另外,如果您可以引用用于运行Method_01和Method_02的Thread变量,则可以让Method_03 \的线程使用Thread.Join来等待两者。但是,这假定那些线程在它们完成Method_01和Method_02的执行时实际上已终止,否则,您需要诉诸我提到的第一个解决方案。     
        为什么不使用静态变量
static volatile int threadRuns
,该变量将用数字线程Method_01和Method_02初始化。 然后,您可以修改这两种方法中的每一种,以在退出前将ѭ19减1:
...
lock(typeof(MyClass)) {
    --threadRuns;
}
...
然后在Method_03的开头,您等待直到threadRuns为0,然后继续:
while(threadRuns != 0)
   Thread.Sleep(10);
我是否正确理解了这个问题?     
        实际上,.Net 4.0中的Barrier类中有一个替代方法。这简化了如何在多个线程之间进行信令。 您可以执行类似以下代码的操作,但这在同步不同的处理线程时最有用。
 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.                
        }
    }
    

要回复问题请先登录注册