Async Lock In Csharp

Ahmed Fouad
Sep 16 · 2 min read

The lock statement was introduced in c# 2.0 to prevent multi threads access to an object at the same time.

In async programming model the goal of the locks is to limit the number of concurrent execution of a block of code to a defined number.

While Microsoft introduced a lot of threads synchronization mechanisms , we will only discuss the SemaphoreSlim in this article.


Example

class DataManger
{
private DbSet<string> _users;
public DataManger(DbSet<string> users)
{
_users = users;
}
public async Task AddUser(string username)
{
await _users.AddAsync(username);
}
}

for some reasons we need to limit the number of calls to addUser method to 3 calls at a time.

static SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(3);
public async Task AddUser(string username)
{
await _semaphoreSlim.WaitAsync();
await _users.AddAsync(username); _semaphoreSlim.Release();
}
static SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(3);

the semaphoreSlim act as lock , we initialize it by setting the maximum number of concurrent request to 3 requests

await _semaphoreSlim.WaitAsync();

if the number of current concurrent requests is less then 3 , it will decrease it by 1 , otherwise it will wait until one of the other threads release.

_semaphoreSlim.Release();

simply release the semaphore so any pending requests or upcoming requests can execute.


Using Aspect Oriented programming

while the semaphoreSlim look easy to use , it come with a cost as it introduce more boilerplates to the code (the semaphore declaration , the waitasync statement at the start of the method and the release at the end) and even more complexities imagine exceptions in _users.AddAsync may be a better idea will to use try finally block.

This can have some dramatic consequences on your code complexity as you will have to declare a semaphore per every method you which to limit access to it.

As a solution to make my code cleaner , I prefer using Postsharp aspects

[Serializable]
public class MethodLockedAttribute : MethodInterceptionAspect
{
private int maximum_concurrency_number;
private static ConcurrentDictionary<int,SemaphoreSlim> SemaphoreSlimRepo=new ConcurrentDictionary<int, SemaphoreSlim>();
public MethodLockedAttribute(int maximumConcurrencyNumber)
{
maximum_concurrency_number = maximumConcurrencyNumber;
}
public override async Task OnInvokeAsync(MethodInterceptionArgs args)
{
SemaphoreSlim semaphore=new SemaphoreSlim(maximum_concurrency_number);
semaphore=SemaphoreSlimRepo.GetOrAdd(args.Method.GetMetadataToken(), semaphore);
await semaphore.WaitAsync();
try
{
await args.ProceedAsync();
}

finally
{
semaphore.Release();
}
}
}

and decorate the target method to be :

[MethodLocked(3)]
public async Task AddUser(string username)
{
await _users.AddAsync(username); }

Ahmed Fouad

Written by

Hello, I’m Ahmed. I’m a software engineer living in Vienna, Austria. I am a fan of technology, web development, and programming. I’m also interested in xamarin

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade