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

我正在尝试使用并发字典la 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是原子的,并确保委托(delegate)不会多次运行?

还有其他想法吗?我希望命名的存储柜速度很快,但在无限制的锁定 namespace 的情况下,不会出现内存压力问题,但对给定名称的争用不大。据我所知,按名称命名的字符串内部锁定永远增长,而且永远都不会清除,并且还有其他副作用。互斥锁是半慢的,并且有各种烦恼(限制为260个字符)。

最佳答案

这将有助于查看IntValue的确切代码,因为这是在AddOrUpdate委托(delegate)中运行的代码。

我认为问题在于代码期望IntValue实例同时存在:

  • 可变,以便锁定与每个字符串关联的单个IntValue实例(尽管引用计数以后会增加)
  • 不可变,因此将IntValue与静态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;
        }
    

    09-04 08:12