我的算法中通过键(字符串)锁定关键部分的缺陷在哪里?



尝试:

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键在那里。

缺陷在哪里,我该如何修复它?

查看 lock 语句(C# 参考(

它基本上分解为

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
}

注意:这只是许多方法中的一个例子,您显然需要根据需要对其进行调整

我注意到的主要问题如下:

※构造函数中超级危险的无限循环,也超级浪费。
※访问私人领域lockedKeys时,您使用不同的对象进行锁定→ 不好

但是,我认为为什么您的代码似乎不起作用是因为您设置的短暂延迟。由于在调试期间从语句单步执行到语句时只有 1 秒的延迟,因此 1 秒已经过去了,它会被释放。

using(new KeyLock(str)){
await Task.Delay(1000);
}

幸运的是,我以前遇到过类似的问题,我也有解决方案。在这里寻找我的小解决方案。

用法:

//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())

最新更新