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;
}