4

Looking at the Barrier class , it allows n threads to rendezvous at a point in time :

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

But so does the CountdownEvent class :

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!

So it seems that the Barrier blocks until n threads are meeting while the CountdownEvent is also blocking until n threads are signaling.

It's kind of confusing( to me ) to learn from it , when should I use which.

Question:

In which (real life scenario) should I need to choose using Barrier as opposed to CountdownEvent (and vice versa) ?

Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • The second program throws `InvalidOperationException` at `_countdown.Signal()`. – Keyur Ramoliya Sep 26 '18 at 16:32
  • 1
    @KeyurRamoliya you should kill the previous executing threads. then you won't get exception ( jsut tested) . see here : https://i.imgur.com/3Ib2i5Z.jpg – Royi Namir Sep 26 '18 at 16:34
  • 1
    Perfect. It's my mistake. – Keyur Ramoliya Sep 26 '18 at 16:37
  • 1
    Somebody could perhaps post example code that shows the difference. But there isn't much point to that when it such an example is already given in the question. Just try to implement the behavior of the 1st sample with CountDownEvent and it ought to be more obvious. – Hans Passant Sep 26 '18 at 16:37

1 Answers1

5

There are some interesting things to note about the two:

  • A CountdownEvent does not have an explicit post-action associated with it; a Barrier does.
  • A CountdownEvent and a Barrier with one phase are roughly equivalent so can be used interchangeably.
  • A Barrier can have multiple phases. As each phase completes, the post-phase action is executed; when that action completes, the next phase begins.

There is a similar question on SO that discusses this behavior, but for the Java equivalents of the C# classes. The answers give several examples, which are valid for the C# equivalents.

That said, consider a real world scenario: checking 3 credit sources for a potential loan to a home buyer. Let's say you don't want to make a decision until you receive all 3 credit scores and evaluate them. You could use either a CountdownEvent (with the code after the Wait() checking the scores) or a single-phased barrier with the score checking code action.

Here's where Barrier would be a better choice: let's say the loan officer wants to also check the home buyer's SO reputation score (because expert users get better credit!) and two other social scores, but only after the credit scores are retrieved (because hey, we don't want check social media if we don't have to).

The neat thing about the Barrier is you can move through the phases in a single method call, which keeps the logic compact and tidy:

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 
    });
Kit
  • 20,354
  • 4
  • 60
  • 103
  • A CountdownEvent does not have an explicit post-action associated with it; a Barrier does. ---- but that's what the last console.write in main does.(at the countdown event example) – Royi Namir Sep 26 '18 at 17:15
  • What I meant was that the `Barrier` takes in an action that is invoked at the end of each phase. A `CountdownEvent` does not take in an action, you have to do your action after the `Wait`. – Kit Sep 26 '18 at 18:00
  • Can you please clarify regarding the loan example - why couldn't I simply use Task.WhenAll? What is the benefit of using a Barrier/CountDownEvent in such case? – BornToCode May 02 '22 at 16:24
  • 1
    @BornToCode for this example you could do that, and would be much simpler. If you "only want to continue when everything is down" that makes sense; if you "want to continue when, say, 4 out of 5 tasks are done" then `Task.WhenAll` wouldn't work. It all depends on your scenario. – Kit May 02 '22 at 18:37