尝试:
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);
}
}
}
使用方式
using(new KeyLock(str))
{
// section that is critical based on str
}
我通过在相同的时间间隔内触发两次方法进行测试
private async Task DoStuffAsync(string str)
{
using(new KeyLock(str))
{
await Task.Delay(1000);
}
}
// ...
await Task.WhenAll(DoStuffAsync("foo"), DoStuffAsync("foo"))
但是,奇怪的是,当我调试时,我看到它第二次直接通过lock
,实际上,lockedKeys.Contains(key)
的求值结果为false
,即使通过调试器窗口中有密钥。
缺陷在哪里,我该如何解决?
答案 0 :(得分:0)
看看lock statement (C# Reference)
它基本上可以分解为
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
在指定 对象上获得排他锁。
您需要做的是保持周围并获得相同的参考。您可能会使用线程安全字典ConcurrentDictionary
public static ConcurrentDictionary<string, object> LockMap = new ConcurrentDictionary<string, object> ();
...
lock (LockMap.GetOrAdd(str, x => new object ()))
{
// do locky stuff
}
注意 :这只是许多方法的一个示例,显然您需要根据需要对其进行调整 >
答案 1 :(得分:0)
我注意到的主要问题如下:
※构造函数中的超级危险无限循环,也非常浪费。
※访问专用字段lockedKeys
时,您使用了不同的对象进行锁定→不好
但是,我认为为什么您的代码似乎不起作用,是因为您设置的延迟很短。当您从语句移至语句时,由于在调试过程中延迟仅为1秒,因此1秒已经过去并且可以处理。
using(new KeyLock(str)){
await Task.Delay(1000);
}
幸运的是,我之前遇到过类似的问题,我也有解决方案。为我的小型解决方案寻找here。
用法:
//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())