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