DEV Community

Debashish Pal
Debashish Pal

Posted on • Edited on

2 1

Solve the scenario - using Thread synchronization in Dotnet - CountDownEvent

Use Case:

Consider this hypothetical scenario. We need to simulate a Marathon Race. So, we have two entities Race & Player

Following are the requirements of each entity.

Race:

  1. Should be able to add the players
  2. Should be able to start the race
  3. It should inform when race starts
  4. It should inform when race ends

Player:

  1. It should have a name
  2. It should declare the fatigue level. Lower is the fatigue, faster the player will run.
  3. It should notify when player starts running
  4. It should notify when player finishes the race

Scenario 1:
When race starts it should wait for all the players to finish the race. Race will only end after that.

All players have a consolation price !!

Scenario 2:
When race starts it should wait for the first three winners !! Race ends after it.

Only first three winners will get a price!! Sorry about other folks.

Take a pause

Think about the solution

?

Think again

Scroll down for the solution

...

...

...

...

...

...

Solution

Run the below code in Linqpad (https://www.linqpad.net/), to see it action. You can create a console project in VS and copy this code as well.

Make sure you press Ctrl+Shift+M, and add the namespace imports i.e. System.Threading & System.Threading.Tasks

We will be using TPL (Task Parallel Library) and Thread Synchronization constructs to solve the problem.

Scenario 1

void Main()
{
//Lower the Fatigue, the faster player will run
Race race = new Race();
//Adding the players with the name & fatigue level
race.AddPlayer("A", 100);
race.AddPlayer("B", 84);
race.AddPlayer("C", 30);
race.AddPlayer("D", 50);
race.AddPlayer("E", 93);
race.AddPlayer("F", 74);
//Start the race
race.Start();
}
internal interface IRace
{
void Start();
void AddPlayer(string name, int fatique);
}
public class Race: IRace
{
private List<Player> lstPlayers;
List<Task> lstRuns;
public void AddPlayer(string name, int fatique){
lstPlayers.Add(new Player { Name = name, Fatigue = fatique});
}
public Race()
{
this.lstPlayers = new List<Player>();
lstRuns = new List<Task>();
}
public void Start()
{
Console.WriteLine("Race started !!");
lstPlayers.ForEach(player =>
{
lstRuns.Add(player.Run());
});
//waiting for all the players to finish the race
Task.WaitAll(lstRuns.ToArray());
Console.WriteLine("Race finished !!");
}
}
internal interface IPlayer
{
Task Run();
int Fatigue { get; set; }
CountdownEvent CountDownEvent { get; set; }
}
internal class Player : IPlayer
{
public string Name { get; set; }
public int Fatigue { get; set; }
public CountdownEvent CountDownEvent { get; set; }
public Task Run()
{
//This task will run on a seperate thread, so it won't block the caller
Task task = new Task(() =>
{
Console.WriteLine($"{Name} started the Race");
do
{
//Simulating the run
Thread.Sleep(100);
Fatigue--;
}
while (Fatigue > 0);
Console.WriteLine($"{Name} finished the Race");
});
//This starts the task
task.Start();
return task;
}
}

Alt Text

If you see the output above, the player who is having the lowest fatigue level comes first and one who comes last is having the highest fatigue.

In this solution we are using Task.WaitAll() to wait for all the players to finish the race, before we end it.

Scenario 2

void Main()
{
//Lower the Fatigue, the faster player will run
Race race = new Race();
//Adding the players with the name & fatigue level
race.AddPlayer("A", 100);
race.AddPlayer("B", 84);
race.AddPlayer("C", 30);
race.AddPlayer("D", 50);
race.AddPlayer("E", 93);
race.AddPlayer("F", 74);
//Start the race
race.Start();
}
internal interface IRace
{
void Start();
void AddPlayer(string name, int fatique);
}
public class Race: IRace
{
private List<Player> lstPlayers;
private CountdownEvent cdEvent;
List<Task> lstRuns;
public void AddPlayer(string name, int fatique){
lstPlayers.Add(new Player { Name = name, Fatigue = fatique});
}
public Race()
{
this.lstPlayers = new List<Player>();
lstRuns = new List<Task>();
//Here we are setting the count i.e. how many players to wait for
cdEvent = new CountdownEvent(3);
}
public void Start()
{
Console.WriteLine("Race started !!");
lstPlayers.ForEach(player =>
{
player.CountDownEvent = cdEvent;
lstRuns.Add(player.Run());
});
//waiting for first 3 players to finish the race
cdEvent.Wait();
Console.WriteLine("Race finished !!");
}
}
internal interface IPlayer
{
Task Run();
int Fatigue { get; set; }
CountdownEvent CountDownEvent { get; set; }
}
internal class Player : IPlayer
{
public string Name { get; set; }
public int Fatigue { get; set; }
public CountdownEvent CountDownEvent { get; set; }
public Task Run()
{
//This task will run on a seperate thread, so it won't block the caller
Task task = new Task(() =>
{
Console.WriteLine($"{Name} started the Race");
do
{
//Simulating the run
Thread.Sleep(100);
Fatigue--;
}
while (Fatigue > 0);
//Signal will decrement the counter of CountDownEvent by one
CountDownEvent.Signal();
Console.WriteLine($"{Name} finished the Race");
});
//This starts the task
task.Start();
return task;
}
}

Alt Text

In this solution we are using CountDownEvent. When a player finishes the race it signal's the CountDownEvent by calling the Signal function/method on it. When it get's the signal, it's counter is decremented by 1. When first 3 players finishes the race, the counter would have been decremented thrice, so the counter would be 0 at this point. Since the Race is waiting on CountDownEvent, as soon as it's counter reaches 0, the race would end.

I hope you have understood how to use synchronization to solve these type of problems.

Happy Coding !!

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay