C# Barrier class is synchronization primitives used in .NET threading. Barrier is used in an algorithm which composed of multiple phases.
In this Barrier synchronization, we have multiple threads working on a single algorithm. Algorithm works in phases. All threads must complete phase 1 then they can continue to phase 2. Until all the threads do not complete the phase 1, all threads must wait for all the threads to reach at phase 1.
When phase 1 is completed by all threads then they will continue to further phases. All the threads must be in same phases in all time. There is no chance of threads to work in different phases.
Below is the sample pseudo code example of algorithm:
Sub BackupDataAlgorithm()
Int[] result;
For I = 1 to 5
Get data from server I
Store data into result[I]
//Phase 1 completed
For J = 1 to 5
Send result[J] data to Backup Server J
//Phase 2 completed
Print “Backup completed”
Above algorithm composed of 2 phases. First phase retrieve data from the different servers and store that into the result variable. In the second phase, it sends all the data to Backup servers. In last statement, it will print algorithm complete message.
Let’s learn some basic about the Barrier class then we’ll implement above algorithm by using Barrier class.
Initializing Barrier Class
When we initialize Barrier class, we initialize with participation count parameter. This parameter specifies how many threads are participating. Below is the code syntax.
Barrier barrier = new Barrier(participantCount: 3);
SignalAndWait Method
This method signals the Barrier object that calling thread reaches at the end of its current phase. This method also waits for all the participating threads to complete the current phase.
Below is the code syntax of SignalAndWait method.
barrier.SignalAndWait();
Barrier Example
Following example shows how we can use Barrier class with the above specified algorithm.
class Program
{
static Barrier barrier = new Barrier(participantCount: 5);
static void Main(string[] args)
{
Task[] tasks = new Task[5];
for (int i = 0; i < 5; ++i)
{
int j = i;
tasks[j] = Task.Factory.StartNew(() =>
{
GetDataAndStoreData(j);
});
}
Task.WaitAll(tasks);
Console.WriteLine("Backup completed");
Console.ReadLine();
}
static void GetDataAndStoreData(int index)
{
Console.WriteLine("Getting data from server: " + index);
Thread.Sleep(TimeSpan.FromSeconds(2));
barrier.SignalAndWait();
Console.WriteLine("Send data to Backup server: " + index);
barrier.SignalAndWait();
}
}
/* Result
* Getting data from server: 0
* Getting data from server: 2
* Getting data from server: 1
* Getting data from server: 3
* Getting data from server: 4
* Send data to Backup server: 1
* Send data to Backup server: 4
* Send data to Backup server: 2
* Send data to Backup server: 0
* Send data to Backup server: 3
* Backup completed */
Dynamic Participation using Barrier class
Sometime we don’t know in advance how many threads we need to use as we are getting data from the database. For that scenarios, Barrier class provides the AddParticipant(s) and RemoveParticipant(s) methods.
- AddParticipant: This method add one participant into the Barrier class
- AddParticipants(int participantCount):This method add number of participants specify in the participant count parameter into the Barrier class.
- RemoveParticipant: This method remove one participant from the Barrier class.
- RemoveParticipants(int participantCount): This method remove number of participants specify in the participant count parameter from the Barrier class.
Below is the code for the above algorithm with dynamic data.
class Program
{
static Barrier barrier = new Barrier(participantCount: 0);
static void Main(string[] args)
{
int totalRecords = GetNumberOfRecords();
Task[] tasks = new Task[totalRecords];
for (int i = 0; i < totalRecords; ++i)
{
barrier.AddParticipant();
int j = i;
tasks[j] = Task.Factory.StartNew(() =>
{
GetDataAndStoreData(j);
});
}
Task.WaitAll(tasks);
Console.WriteLine("Backup completed");
Console.ReadLine();
}
static int GetNumberOfRecords()
{
return 20;
}
static void GetDataAndStoreData(int index)
{
Console.WriteLine("Getting data from server: " + index);
Thread.Sleep(TimeSpan.FromSeconds(2));
barrier.SignalAndWait();
Console.WriteLine("Send data to Backup server: " + index);
barrier.SignalAndWait();
}
}