Update
I thought that you might be able to use Interlocked.CompareExchange to do the job that you want, but it turns out AutoResetEvent works better. Here is a working sample:
Actual locking code
public class LockAcquisition<T> : IDisposable
{
private static readonly ConcurrentDictionary<T, LockWrapper> currentRequests = new ConcurrentDictionary<T, LockWrapper>();
private readonly LockWrapper _currentLock;
public LockAcquisition(T key)
{
_currentLock = currentRequests.GetOrAdd(key, new LockWrapper());
_currentLock.AcquireLock();
}
public void Dispose()
{
if (_currentLock != null)
{
_currentLock.Dispose();
}
}
~LockAcquisition()
{
Dispose();
}
private class LockWrapper : IDisposable
{
private readonly AutoResetEvent _locker = new AutoResetEvent(true);
public void AcquireLock()
{
_locker.WaitOne();
}
public void Dispose()
{
_locker.Set();
}
~LockWrapper()
{
Dispose();
}
}
}
Wrapped to use a string as the key
public class LockAcquisition : LockAcquisition<String>
{
public LockAcquisition(string key)
: base(key)
{
}
}
Testing code
internal class Program
{
private static void Main(string[] args)
{
Run();
Console.ReadKey();
}
private static async void Run()
{
const string hash = "123456";
const string hash2 = "1234567";
using (new LockAcquisition(hash))
{
using (new LockAcquisition(hash2))
{
using (new LockAcquisition(hash)) // This will deadlocked
{
}
await Task.Delay(TimeSpan.FromSeconds(2));
Console.WriteLine("Done 2");
}
await Task.Delay(TimeSpan.FromSeconds(2));
Console.WriteLine("Done 1");
}
}
}