Awaiting inside c# lock block???

Awaiting in C# lock block?

So we all have been in a situation when we were writing our scalable and maintainable C# applications taking advantage of async await and we wanted to synchronise access to shared resources using lock block. Only to find out that it is impossible to await when we are inside lock block. If we try we get an error like this: Cannot await in the body of a lock statement. This is purposely not allowed because a lot of bad thingsā„¢ can happen if we did so. For example after await we can continue in a different thread and we might end up unlocking a different thread than the one that took that lock in a first place. There are some other problems related to awaiting inside lock like lock ordering inversions but I won’t go into details. Instead I’ll offer a solution to this problem!

We will take advantage of SemaphoreSlim class that was introduced in .NET Framework 4.0 in System.Threading namespace. If we think about it lock is just a binary semaphore protecting a critical section that is a body of our lock block. And SemaphoreSlim is counting semaphore that supports async await. So the code for our AsyncLock would look something like this:

[code language=”csharp” gutter=”false”]
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyApp
{
public class AsyncLock : IDisposable
{
private SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);

public async Task<AsyncLock> LockAsync()
{
await _semaphoreSlim.WaitAsync();
return this;
}

public void Dispose()
{
_semaphoreSlim.Release();
}
}
}
[/code]

A sample usage would look like this:

[code language=”csharp” gutter=”false”]
private static readonly AsyncLock _mutex = new AsyncLock();

using(await _mutex.LockAsync())
{
// Critical section… You can await here!
}
[/code]

Here we are creating an instance of SemaphoreSlim and wrapping it inside IDisposable so that we could use using statement for our critical section. Semaphore has a max count of 1 meaning that only one thread can hold a lock without blocking (asynchronously) at any given time. We are decreasing a CurrentCount property of the semaphore every time we call LockAsync and increasing it every time Dispose is called. And thats it! The usage is very simple and the syntax is very similar to the one of a lock statement.

Hi, my name is Andrius. Welcome to my blog! I'm freelance mobile (C# Xamarin) developer currently learning Swift. I also do some ASP.NET Core Web API work as well. I blog mostly about work related subjects. If you would like to contact me for any reason shoot me an email at andrius [at] applications.lt

Site Footer