Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Async Lock In C#

0.00/5 (No votes)
16 Sep 2019 1  
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.

Image 1

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);
       }

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here