Lock for Async Methods

Despite the intended nature, we seldom might come across situation where we would want to place a lock around an async statement and make it behave differently, especially when accessed from different threads. Let’s try to mock the scenario first to demonstrate the issue.

The lock-async Problem

Consider the following code snippet.

void Main()
{
     Parallel.For(0,5,async (x,y)=>{await TestMethod();});
}

async Task<bool> TestMethod()
{
Console.WriteLine("In");
// Do some tasks here. For demonstration purpose we will use Task.Delay to mock a long running task.
await Task.Delay(2000);
Console.WriteLine("Out");
return await Task.FromResult(true);
}

The output of the above code would be as following, understandably allowing multiple threads to access the ‘intended long running process code‘.

In
In
In
In
In
Out
Out
Out
Out
Out

The most common workaround is placing a lock statement around the code. Try placing a lock around the await statement and you would surprised to know you are not allowed to do so.

“Cannot await in the body of a lock statement”

This is where the trouble starts.

The Solution

The workaround for the issue with Semaphore. Instead of lock, you could use a semaphore to ensure only one/limited threads access the async method at a time.

Let us rewrite the code with semaphore now.

void Main()
{
       Parallel.For(0,5,async (x,y)=>{await TestMethod();});
}
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,1);
async Task<bool> TestMethod()
{
await semaphoreSlim.WaitAsync();
try
{
Console.WriteLine("In");
await Task.Delay(2000);
Console.WriteLine("Out");
}
finally
{
semaphoreSlim.Release();
}
return await Task.FromResult(true);
}

As you can observe, the compile would accept this solution as desired. The output of following can seen below.

In
Out
In
Out
In
Out
In
Out
In
Out

Enjoy coding

Advertisements