Hello folks, in this blog we will understand how we can use Counting Semaphores with Async and Await in C#.
Semaphores :
We can define the term “semaphore” as an integer variable that is shared by multiple threads/processes which prevent multiple processes from accessing a critical section. Wait and Signal are two atomic operations that enable the semaphore to provide process synchronization.
Let’s take a scenario where we have to write to a file. We can not have multiple processes accessing the same file at the same time. We can safely assume opening, writing, and closing the file as a part of the critical section. To prevent the data race we can use a semaphore to lock multiple processes/threads from entering the critical section at the same time.
Semaphores are of two types:
- Binary Semaphore –
We can also refer it as a mutex lock. It has two possible values: 0 and 1. We can use it to implement the solution for critical section problems. - Counting Semaphore –
Counting Semaphore can have value ranging from [0,n] where n can have value greater than 1.
The syntax for semaphore initialization in C#
We passed two values to the constructor of the Semaphore class while initialization of our Semaphore object. InitialCount and MaximumCount are the two values.
The InitialCount parameter sets the value for the Int32 variable that specifies the initial number of concurrent requests that can be granted. The MaximumCount parameter specifies the maximum number of concurrent requests for the semaphore.
Code snippet for a sample Semaphore implementation in C# :
using System;
using System.Threading;
namespace SemaphoreDemo
{
class Program
{
public static Semaphore semaphore = new Semaphore(2, 3);
static void Main(string[] args)
{
for (int i = 1; i <= 10; i++)
{
Thread threadObject = new Thread(DoSomeTask)
{
Name = "Thread " + i
};
threadObject.Start(i);
}
Console.ReadKey();
}
static void DoSomeTask(object id)
{
Console.WriteLine(Thread.CurrentThread.Name + " Wants to Enter into Critical Section for processing");
try
{
//Blocks the current thread until the current WaitHandle receives a signal.
semaphore.WaitOne();
Console.WriteLine("Success: " + Thread.CurrentThread.Name + " is Doing its work");
Thread.Sleep(5000);
Console.WriteLine(Thread.CurrentThread.Name + "Exit.");
}
finally
{
//Release() method to releage semaphore
semaphore.Release();
}
}
}
}
Result :
Async and Await :
With the help of the async and await keywords in C#, asynchronous programming has become quite popular in recent years. Let’s consider a simple application that downloads a file and as we know download time differs due to various factors. If the application performs all the functions synchronously then we can face an issue of the application being unresponsive. You may think why would the application be unresponsive? If we click on the button that downloads the files, we are starting the download process that can take time to complete. As we were waiting for the download to be complete, we cannot perform any other action through the application at this time and the application becomes unresponsive which leads to a bad experience for us.
In this situation, asynchronous programming comes in handy. We can use async and await to make the download function perform its task asynchronically which would prevent it from making the UI unresponsive.
using System;
using System.Threading.Tasks;
using System.Threading;
namespace Demo{
class SemaphoreProgram
{
public static Semaphore semaphore = new Semaphore(5, 5);
static void Main(string[] args)
{
for(int i = 1; i <= 10; i++)
{
_ = DoSomeTaskAsync(i);
}
Console.ReadKey();
}
static async Task<bool> DoSomeTaskAsync(int i)
{
try
{
semaphore.WaitOne();
Console.WriteLine("Success: " + i + " is Doing its work");
_ = await Task.Run(() => DoNothing(i));
Console.WriteLine(i + " Exit.");
}
finally
{
semaphore.Release();
}
return true;
}
static string DoNothing(int i)
{
Console.WriteLine("Starting to do nothing for " + i);
Thread.Sleep(10000);
Console.WriteLine("DoNothing " + i);
return "";
}
}
}
The basic logic behind using counting semaphore with Async and Await is to prevent multiple tasks from entering the critical section.
Code Result :