So now, that we know what a Semaphore is we may go ahead and replace the lock with a Semaphore. In our case, the SemaphoreSlim class is the ideal data type to use since we will be using it in a single process.
// Replacing the Lock with a Semaphore
//
// Article: // https://blog.cdemi.io/async-waiting-inside-c-sharp-locks/
//
// So now, that we know what a Semaphore is we may go ahead and replace the lock
// with a Semaphore. In our case, the SemaphoreSlim class is the ideal data type
// to use since we will be using it in a single process.
//
// It is vital to always release the Semaphore when you are ready, this is why
// it is suggested to be placed inside a try...finally clause[3].
//
// Calling WaitAsync on the semaphore produces a task that will be completed
// when that thread has been granted access to the Semaphore.
// Instantiate a Singleton of the Semaphore with a value of 1.
// This means that only 1 thread can be granted access at a time.
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
// Asynchronously wait to enter the Semaphore. If no-one has been granted access
// to the Semaphore, code execution will proceed, otherwise this thread waits
// here until the semaphore is released
await semaphoreSlim.WaitAsync();
try
{
await Task.Delay(1000);
}
finally
{
// When the task is ready, release the semaphore. It is vital to ALWAYS
// release the semaphore when we are ready, or else we will end up with a
// Semaphore that is forever locked.
//
// This is why it is important to do the Release within a try...finally
// clause; program execution may crash or take a different path, this way
// you are guaranteed execution
semaphoreSlim.Release();
}
// ---------------------------------------------------------------------
// Example 2
// ---------------------------------------------------------------------
//
// https://davidsekar.com/c-sharp/dont-lock-on-async-tasks
// System.Threading assembly comes with Semaphore and SemaphoreSlim that helps
// you to control concurrent access to the shared resource.
public class LockTest
{
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
public async Task RecordSuccess(int batchId)
{
await _lock.WaitAsync();
try
{
// Record a success in database
var success = GetCurrentSuccessCountFromDB();
SaveSuccessCountToDB(success + 1);
}
finally
{
_lock.Release();
}
}
public async Task RecordFailure(int batchId)
{
await _lock.WaitAsync();
try
{
// Record a failure in database
var success = GetCurrentFailureCountFromDB();
SaveFailureCountToDB(success + 1);
}
finally
{
_lock.Release();
}
}
}