This worked well for me as detailed here:
SemaphoreSlim Class
Semaphores are of two types: local semaphores and named system semaphores.
The former is local to an app. The latter is visible throughout the operating system and is suitable for inter-process synchronization.
The SemaphoreSlim is a lightweight alternative to the Semaphore class that doesn't use Windows kernel semaphores. Unlike the Semaphore class, the SemaphoreSlim class doesn't support named system semaphores.
You can use it as a local semaphore only. The SemaphoreSlim class is the recommended semaphore for synchronization within a single app.
public class ResourceLocker
{
private Dictionary<string, SemaphoreSlim> _lockers = null;
private object lockObj = new object();
public ResourceLocker()
{
_lockers = new Dictionary<string, SemaphoreSlim>();
}
public SemaphoreSlim GetOrCreateLocker(string resource)
{
lock (lockObj)
{
if (!_lockers.ContainsKey(resource))
{
_lockers.Add(resource, new SemaphoreSlim(1, 1));
}
return _lockers?[resource];
}
}
public bool ReleaseLocker(string resource)
{
lock (lockObj)
{
if (_lockers.ContainsKey(resource))
{
var locker = _lockers?[resource];
if (locker != null)
{
locker.Release();
return true;
}
_lockers.Remove(resource);
}
return false;
}//lock
}
}
Usage
var resource = "customResource";
var someObject = new SomeObject();
SomeResponse response = null;
var resourceLocker = new ResourceLocker();
try
{
var semaSlim = resourceLocker.GetOrCreateLocker(resource);
semaSlim.Wait();
response = someObject.DoSomething();
}
finally
{
resourceLocker.ReleaseLocker(resource);
}
Async
Task.Run(async ()=>{
var semaSlim = resourceLocker.GetOrCreateLocker(resource);
await semaSlim.WaitAsync();
response = someObject.DoSomething();
resourceLocker.ReleaseLocker(resource);
});