CountdownEvent与Barrier的多线程用法?

时间:2018-09-26 16:04:06

标签: c# multithreading

通过查看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(反之亦然)?

1 个答案:

答案 0 :(得分:4)

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

  • CountdownEvent没有与之关联的显式后处理; 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 
    });