Basic Version
Extended Version
A version of the LockAsync method that claims to be completely deadlock-safe, can be found in (from the 4th revision of this answer (bysuggested by Jez).
using System; using System.Threading; using System.Threading.Tasks; public class SemaphoreLocker { private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); public async Task LockAsync(Func<Task> worker) { var isTaken = false; try { do { try { } finally { isTaken = await _semaphore.WaitAsync(TimeSpan.FromSeconds(1)); } } while (!isTaken); await worker(); } finally { if (isTaken) { _semaphore.Release(); } } } // overloading variant for non-void methods with return type (generic T) public async Task<T> LockAsync<T>(Func<Task<T>> worker) { var isTaken = false; try { do { try { } finally { isTaken = await _semaphore.WaitAsync(TimeSpan.FromSeconds(1)); } } while (!isTaken); return await worker(); } finally { if (isTaken) { _semaphore.Release(); } } } } Usage:
public class Test { private static readonly SemaphoreLocker _locker = new SemaphoreLocker(); public async Task DoTest() { await _locker.LockAsync(async () => { // [async] calls can be used within this block // to handle a resource by one thread. }); // OR var result = await _locker.LockAsync(async () => { // [async] calls can be used within this block // to handle a resource by one thread. }); } }