2012-02-15 6 views
0

私は特定のオブジェクト(数値IDで識別される)に対して複数のオープン接続を持つことは絶対にありませんが、異なるコンシューマがそれらを開いたり閉じたりすることがあるバックエンドシステムとインタフェースしています互いに独立している。ここでの問題で、今ファクトリメソッドでロックする

private Dictionary<ulong, IFoo> _openItems = new Dictionary<ulong, IFoo>(); 
private object _locker = new object(); 

public IFoo Open(ulong id) 
{ 
    lock (_locker) 
    { 
     if (!_openItems.ContainsKey(id)) 
     { 
      _openItems[id] = _nativeResource.Open(id); 
     } 

     _openItems[id].RefCount++; 

     return _openItems[id]; 
    } 
} 

public void Close(ulong id) 
{ 
    lock (_locker) 
    { 
     if (_openItems.ContainsKey(id)) 
     { 
      _openItems[id].RefCount--; 
      if (_openItems[id].RefCount == 0) 
      { 
       _nativeResource.Close(id); 
       _openItems.Remove(id); 
      } 
     } 
    } 
} 

は大雑把に、私はこのようなファクトリクラスのフラグメントを持っています。私の場合、_nativeResource.Openはです。非常にです。ここでのロックはむしろ素朴で、同時の.Open呼び出しの数が多い場合は、(おそらく)異なるIDを参照していて重複していなくても非常に遅くなる可能性があります。特に、_openItemsキャッシュ。

ロックを構成して、特定のIDへの同時アクセスを防止し、すべての発信者に防ぐことはできません。

+0

グローバルオブジェクト_lockerにないIDをロックしたいとします。単一のIDに対応するオブジェクトを見つけようとします。可能であれば、独自のオブジェクトセットを構築します。 – mathk

答えて

3

あなたが調べたいのは、ストライプロック戦略です。アイデアはM個のアイテム(あなたのケースでは可能なID)にN個のロックを共有し、任意のIDに対して常に選択されたロックが同じものであるようにロックを選択することです。あなたは、.NET 4にある場合、あなたは試みることができる

// Assuming the allLocks class member is defined as follows: 
private static AutoResetEvent[] allLocks = new AutoResetEvent[10]; 


// And initialized thus (in a static constructor): 
for (int i = 0; i < 10; i++) { 
    allLocks[i] = new AutoResetEvent(true); 
} 


// Your method becomes 
var lockIndex = id % allLocks.Length; 
var lockToUse = allLocks[lockIndex]; 

// Wait for the lock to become free 
lockToUse.WaitOne(); 
try { 
    // At this point we have taken the lock 

    // Do the work 
} finally { 
    lockToUse.Set(); 
} 
+0

これは、.WaitOne()ではなくlockToUse.Reset()であるべきではありませんか? – Joe

+0

AutoResetEventの 'WaitOne'は、イベントが設定されるまでブロックされます(誰かが' Set'を呼び出します、または初めて待っているときはコンストラクタに渡された 'true'で作成されます)。イベントがSetになるとすぐに、WaitOneを呼び出す最初のスレッドが進行し、(アトミックに)イベントがリセットされ、他のすべてのWaitingスレッドがブロックされます。 –

+0

ああ、そうだ。私はあなたが本当に(コンストラクタ内で)それらをすべてデフォルトにしたところでそれを逃しました。ありがとう。これを試してみよう。 – Joe

1

:単純に、NでMを分割し、残りを取り、そのインデックスにロックを使用しディビジョンこの技術のためにロックを選択する古典的な方法は、モジュロですこれらの線に沿って何かを持つConcurrentDictionary:誰もがそれを持つ任意の明白な問題を見ることができる場合、私に知らせてください

private ConcurrentDictionary<ulong, IFoo> openItems = new ConcurrentDictionary<ulong, IFoo>(); 
private object locker = new object(); 

public IFoo Open(ulong id) 
{ 
    var foo = this.openItems.GetOrAdd(id, x => nativeResource.Open(x)); 

    lock (this.locker) 
    { 
     foo.RefCount++; 
    } 

    return foo; 
} 

public void Close(ulong id) 
{ 
    IFoo foo = null; 

    if (this.openItems.TryGetValue(id, out foo)) 
    { 
     lock (this.locker) 
     { 
      foo.RefCount--; 

      if (foo.RefCount == 0) 
      { 
       if (this.openItems.TryRemove(id, out foo)) 
       { 
        this.nativeResource.Close(id); 
       } 
      } 
     } 
    } 
} 

+0

ConcurrentDictionary.GetOrAddのファクトリメソッドはスレッドセーフではありません。 nativeResource.Open *は、この実装を使用して複数回*実行できます。 [これをチェック](http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2535774-additional-overload-to-system-concurrent-concurren) – Anastasiosyal

関連する問題