2013-06-25 81 views
7

ConcurrentDictionary Pitfall - Are delegates factories from GetOrAdd and AddOrUpdate synchronized?注意AddOrUpdate不是原子的(並且不能保證委託不會運行多次)。atomic 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是原子的,上面的代碼就可以完成它的工作,並且條目將被適當地刪除。

請注意,通過鍵+ val擴展方法TryRemove(key,val)的條件刪除的使用提到了here。另外,IntObject是一個int的簡單可變對象包裝。

我有什麼選擇?是否有任何併發​​字典實現具有1.原子條件(關鍵和值)刪除和2. AddOrUpdate是原子的並確保委託不會運行多次?

有其他想法嗎?我希望命名的儲物櫃速度很快,但是由於無限制的鎖定名稱空間而沒有給定名稱的爭用,所以沒有內存壓力問題。據我所知,字符串通過名字鎖定interning鎖定會永遠增長,永遠不會被清理,並有其他副作用。互斥體是半緩慢的並且有各種煩惱(260個字符限制)。

+0

你可能會使用類型爲'ConcurrentDictionary >'的字典,正如我在本答案中所述http://stackoverflow.com/a/12611341/1236734 –

回答

1

這將有助於查看IntValue的確切代碼,因爲這是在AddOrUpdate委託中運行的代碼。

我認爲這個問題是代碼預計IntValue情況下,既:

  • 可變的,以鎖定與每個字符串的單個IntValue實例(儘管引用計數以後遞增)
  • 永恆使比較IntValue s到靜態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; 
    }