查看Barrier类,它允许n线程在某个时间点集合。

static Barrier _barrier = new Barrier(3);
static void Main()
{
    new Thread(Speak).Start();
    new Thread(Speak).Start();
    new Thread(Speak).Start();
}
static void Speak()
{
    for (int i = 0; i < 5; i++)
    {
        Console.Write(i + " ");
        _barrier.SignalAndWait();
    }
}
//OUTPUT: 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4

但是CountdownEvent类也是如此:
static CountdownEvent _countdown = new CountdownEvent(3);
static void Main()
{
    new Thread(SaySomething).Start("I am thread 1");
    new Thread(SaySomething).Start("I am thread 2");
    new Thread(SaySomething).Start("I am thread 3");
    _countdown.Wait(); // Blocks until Signal has been called 3 times
    Console.WriteLine("All threads have finished speaking!");
}
static void SaySomething(object thing)
{
    Thread.Sleep(1000);
    Console.WriteLine(thing);
    _countdown.Signal();
}


// output :
I am thread 3
I am thread 1
I am thread 2
All threads have finished speaking!

因此,似乎Barrier阻塞,直到n线程开会为止
CountdownEvent也将阻塞,直到n线程发出信号为止。

从中学习有点使我困惑,我什么时候应该使用它。

问题:

在哪种情况下(实际情况),我应该选择使用Barrier而不是CountdownEvent(反之亦然)?

最佳答案

关于这两者,有一些有趣的事情要注意:

  • CountdownEvent没有与之关联的明确后置 Action 。 Barrier可以。
  • 一个相位的CountdownEventBarrier大致相等,因此可以互换使用。
  • Barrier可以具有多个阶段。每个阶段完成后,将执行后阶段操作;该操作完成后,下一阶段开始。

  • SO上有一个similar question讨论了此行为,但适用于C#类的Java等效项。答案给出了几个示例,这些示例对于C#等效项有效。

    也就是说,请考虑一个现实世界的场景:检查3个信贷来源以寻找向购房者的潜在贷款。假设您在获得所有3个信用评分并对其进行评估之前,不想做出决定。您可以使用CountdownEvent(在Wait()之后的代码检查分数)或在分数检查代码操作中使用单阶段障碍。

    在这里Barrier会是一个更好的选择:假设贷款员还希望检查购房者的SO信誉评分(因为专家用户获得了更好的信用!)和其他两个社交评分,但是仅在检索信用评分之后(因为嘿,如果不需要,我们不想检查社交媒体)。

    关于Barrier的一件整洁的事情是,您可以在单个方法调用中浏览各个阶段,从而使逻辑紧凑而整洁:
    var barrier = new Barrier(participantCount: 3, b => LogScoreAndPossiblyEvaluate(b));
    var credit = new int[3];
    var social = new int[3];
    
    void LogScoreAndPossiblyEvaluate(Barrier b)
    {
        Log.Info("Got scores for {b.CurrentPhaseNumber == 1 ? "credit" : "social"} phase");
         ...
        if (b.CurrentPhaseNumber == 2 && SomeComplexCalculationWithSixScores() == LoanResult.Approved)
            LoanMoney();
    }
    
    ...
    
    for (int i=0; i<3; i++)
        Task.Run(() => {
            credit[i] = GetCreditScore(CreditSource(i);
            barrier.SignalAndWait();
    
            social[i] = GetSocialScore(SocialSource(i);
            barrier.SignalAndWait();
    
            // all phases done at this point
        });
    

    09-16 01:40