Async Lock In C#






4.27/5 (4 votes)
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..
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); 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); }