【问题标题】:atomic addorupdate (trying to write named locker using concurrent dictionary)atomic addorupdate(尝试使用并发字典编写命名储物柜)
【发布时间】:2013-06-21 15:41:46
【问题描述】:

ConcurrentDictionary Pitfall - Are delegates factories from GetOrAdd and AddOrUpdate synchronized? 指出 AddOrUpdate 不是原子的(并且不能保证委托不会多次运行)。

我正在尝试使用并发字典实现名称锁定实现,例如 here,但字典不应永远增长,如下所示:

public class ConcurrentDictionaryNamedLocker : INamedLocker
{
    // the IntObject values serve as the locks and the counter for how many RunWithLock jobs 
    // are about to enter or have entered the critical section.
    private readonly ConcurrentDictionary<string, IntObject> _lockDict = new ConcurrentDictionary<string, IntObject>();
    private static readonly IntObject One = new IntObject(1);
    private readonly Func<string, IntObject, IntObject> _decrementFunc = (s, o) => o - 1;
    private readonly Func<string, IntObject, IntObject> _incrementFunc = (s, o) => o + 1;
    private readonly Func<string, IntObject> _oneFunc = s => new IntObject(1);
    private readonly Func<string, IntObject> _zeroFunc = s => new IntObject(0);

    public TResult RunWithLock<TResult>(string name, Func<TResult> body)
    {
        name = name.ToLower();
        TResult toReturn;
        lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc))
        {
            toReturn = body();
            if (!_lockDict.TryRemove(name, One))
                _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc);
        }
        return toReturn;
    }

    public void RunWithLock(string name, Action body)
    {
        name = name.ToLower();
        lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc))
        {
            body();
            if (!_lockDict.TryRemove(name, One))
                _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc);
        }
    }
}

但问题是 AddOrUpdate 不是原子的,所以我看到当存在争用时条目通常不会被删除。我相当肯定,如果 AddOrUpdate 是原子的,那么上面的代码将完成它的工作并且条目将被适当地删除。

注意here 提到的通过 key+val 扩展方法 TryRemove(key,val) 使用条件删除。此外,IntObject 是一个简单的 int 可变对象包装器。

我有哪些选择?是否有任何并发​​字典实现具有 1. 原子条件(键和值)删除和 2. AddOrUpdate 是原子的并确保委托不会多次运行?

还有其他想法吗?我希望命名的储物柜速度快,但在给定无限锁定命名空间但对给定名称没有太多争用的情况下没有内存压力问题。据我所知,按名称进行的字符串实习锁定会永远增长,并且永远不会被清理并具有其他副作用。并且互斥体是半慢的并且有各种烦恼(260 个字符限制)。

【问题讨论】:

标签: .net concurrency concurrentdictionary


【解决方案1】:

查看IntValue 的确切代码会有所帮助,因为这是在AddOrUpdate 委托中运行的代码。

我认为问题在于代码期望 IntValue 实例是两者:

  • 可变以锁定与每个字符串关联的单个 IntValue 实例(尽管稍后引用计数会增加)
  • 不可变,因此将 IntValues 与静态 One 进行比较可作为删除标准

如果我更改代码以使 IntValue 支持不可变零,这似乎可行:

private readonly Func<string, IntObject> _zeroFunc = s => IntObject.Zero;

...

public void RunWithLock(string name, Action body)
{
    name = name.ToLower();
    lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc))
    {
        body();
        _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc);
        _lockDict.TryRemove(name, IntObject.Zero);
    }
}

但我选择像这样以非操作员的身份实现IntValue 方法:

    internal IntObject Dec(int p)
    {
        var newVal = Interlocked.Decrement(ref value);
        if (newVal == 0) return Zero;
        return this;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多