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

时间:2018-10-05 04:19:55

标签: c# multithreading asynchronous locking mutex

尝试:

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,即使通过调试器窗口中有密钥。

缺陷在哪里,我该如何解决?

2 个答案:

答案 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);
}

Enter(Object)

  

指定 对象上获得排他锁。


您需要做的是保持周围并获得相同的参考。您可能会使用线程安全字典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())