C# 如何确保一个线程在特定数量的其他线程运行结束后运行?

C# 如何确保一个线程在特定数量的其他线程运行结束后运行?,c#,multithreading,synchronization,semaphore,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()”形式。

我有一门C#课,像这样:

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

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

“Method_01()”或“Method_02()”的线程是在创建方法_03()的线程之前创建的,但不能保证在“Method_03()的线程开始之前结束。我的意思是“Method_01()”或“Method_02()”将失去它们的CPU周期,“Method_03”将获得CPU周期,并将完全结束


在“Start()”方法中,我知道应该创建和运行“method_01”和“method_02()”的线程总数。问题是,我正在寻找一种使用信号量或互斥量的方法,以确保“Method_03()”的第一条语句将恰好在运行“Method_01()”或“Method_02()”的所有线程结束后运行,使用线程数初始化,将运行方法\u 01和方法\u 02。 然后,在退出之前,修改这两种方法中的每一种,以减少
threadRuns

...
lock(typeof(MyClass)) {
    --threadRuns;
}
...
然后在方法_03的开头,等待threadRuns为0,然后继续:

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

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

听起来您需要做的是为方法\u 01和方法\u 02中的每一个创建一个ManualResetEvent(初始化为unset)或一些其他WatHandle,然后在句柄集上使用方法\u 03的线程


或者,如果可以引用用于运行方法\u 01和方法\u 02的线程变量,则可以让方法\u 03的线程使用Thread.Join来等待这两个线程。但是,这假设这些线程在完成方法_01和方法_02的执行时实际终止-如果它们没有终止,则需要使用我提到的第一个解决方案。

想到的三个选项是:

  • 保留一个
    线程
    实例数组,并从
    方法\u 03
    对所有实例调用
    Join
  • 使用单个
    CountdownEvent
    实例并从
    Method\u 03
    调用
    Wait
  • 为每个
    Method\u 01
    Method\u 02
    调用和调用
    WaitHandle.WaitAll
    分配一个
    ManualResetEvent
    (这不是很好扩展)
我更喜欢使用
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.
  }
}
公共类MyClass
{
私有倒计时事件m_Finished=新倒计时事件(0);
公开作废开始()
{
m_Finished.AddCount();//递增以指示此线程处于活动状态。
for(int i=0;i<线程数;i++)
{
m_Finished.AddCount();//递增以指示另一个活动线程。
新线程(方法_01).Start();
}
for(int i=0;i<线程数;i++)
{
m_Finished.AddCount();//递增以指示另一个活动线程。
新线程(方法_02).Start();
}
新线程(方法_03).Start();
m_Finished.Signal();//表示此线程已完成的信号。
}
私有无效方法_01()
{
尝试
{
//在这里添加您的逻辑。
}
最后
{
m_Finished.Signal();//表示此线程已完成的信号。
}
}
私有无效方法_02()
{
尝试
{
//在这里添加您的逻辑。
}
最后
{
m_Finished.Signal();//表示此线程已完成的信号。
}
}
私有无效方法_03()
{
m_Finished.Wait();//等待所有信号。
//在这里添加您的逻辑。
}
}

实际上,在.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.
        }
    }
公共级同步器
{
私人壁垒(私人壁垒),;
公共void开始(int numThreads)
{
_屏障=新屏障((numThreads*2)+1);
for(int i=0;i
我还想建议,由于您的问题陈述非常抽象,通常使用countdownevent解决的实际问题现在使用新的并行或PLINQ功能可以更好地解决。如果您在代码中实际处理一个集合或其他内容,您可能会遇到如下情况

 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.                
        }
    }
公共级同步器
{
公共作废开始(列表集合)
{
新线程(()=>Method3(集合));
}
公共无效方法1(someClass)
{
//做一些工作。
}
公共无效方法2(someClass)
{
//做一些其他的工作。
}
公共作废方法3(列表集合)
{
//在平行线程中对每个项目进行处理。
Parallel.ForEach(集合,x=>{Method1(x);Method2(x);});
//对整个集合做一些工作,比如排序或其他。
}
}

对于来说,这似乎是一份完美的工作
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();