Attempt:
public class KeyLock : IDisposable
{
private string key;
private static ISet<string> lockedKeys = new HashSet<string>();
private static object locker1 = new object();
private static object locker2 = new object();
public KeyLock(string key)
{
lock(locker2)
{
// wait for key to be freed up
while(lockedKeys.Contains(key));
this.lockedKeys.Add(this.key = key);
}
}
public void Dispose()
{
lock(locker)
{
lockedKeys.Remove(this.key);
}
}
}
to be used like
using(new KeyLock(str))
{
// section that is critical based on str
}
I test by firing the method twice in the same timespan
private async Task DoStuffAsync(string str)
{
using(new KeyLock(str))
{
await Task.Delay(1000);
}
}
// ...
await Task.WhenAll(DoStuffAsync("foo"), DoStuffAsync("foo"))
but, strangely enough, when I debug I see that the second time it goes straight through the lock
and in fact somehow lockedKeys.Contains(key)
evaluates to false
even through I can see in my debugger windows that the key is there.
Where is the flaw and how do I fix it?
Take a look at lock statement (C# Reference)
It basically breaks down to
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
Acquires an exclusive lock on the specified object .
What you need to do instead, is keep around and obtain the same reference. You could probably use a thread safe Dictionary ConcurrentDictionary
public static ConcurrentDictionary<string, object> LockMap = new ConcurrentDictionary<string, object> ();
...
lock (LockMap.GetOrAdd(str, x => new object ()))
{
// do locky stuff
}
Note : This is just one example of many ways to do this, you will obviously need to tweak it for your needs
The main problems that I notice are as follows:
※Super dangerous infinite loop in the constructor, and super wasteful as well.
※When accessing the private field lockedKeys
, you use different objects to lock on→ Not good
However, why your code does not seem to be working I think is because of the short delay you set. Since it is only 1 second of delay during the debugging when you step from statement to statement, 1 second already passes and it gets disposed.
using(new KeyLock(str)){
await Task.Delay(1000);
}
Luckily for you, I came across a similar problem before and I have a solution, too. Look here for my small solution.
Usage:
//Resource to be shared
private AsyncLock _asyncLock = new AsyncLock();
....
....
private async Task DoStuffAsync()
{
using(await _asyncLock.LockAsync())
{
await Task.Delay(1000);
}
}
// ...
await Task.WhenAll(DoStuffAsync(), DoStuffAsync())
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.