atomic addorupdate(尝试使用并发字典编写命名的locker)

Tra*_*isK 7 .net concurrency concurrentdictionary

ConcurrentDictionary陷阱 - 来自GetOrAdd和AddOrUpdate的委托工厂是否同步?注意到AddOrUpdate不是原子的(并且不能保证代理不会多次运行).

我想实现的名称使用并发字典一拉锁定实现这里,但如果字典不应该永远增长,就像这样:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

请注意使用此处提到的key + val扩展方法TryRemove(key,val)进行条件删除.此外,IntObject是int的简单可变对象包装器.

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

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

Pet*_*art 1

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

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

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

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

private readonly Func<string, IntObject> _zeroFunc = s => IntObject.Zero;
Run Code Online (Sandbox Code Playgroud)

...

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

IntValue但我选择以非运算符的身份实现这样的方法:

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