2016-08-15 5 views
0

私のコードでは、 'key'はオブジェクトで、 'value'は一意の第三者のライブラリによって提供される前記オブジェクトへの接続パラメータを含む。サードパーティのライブラリを介してイベントがスローされたときにユニークキーがイベント引数の1つとなるように、接続パラメータが格納されます。これにより、さらにコードを処理する正しいオブジェクトを見つけることができます。次のように私が持っているConcurrentDictionaryのエントリとC#の辞書自体の階層的なロック戦略

問題がある:時々

  • 、同時辞書には、処理を必要とするオブジェクトのリフレッシュリストを更新する必要があります。

  • 個々のオブジェクトは、処理中にロックを必要とします。スローされたイベントは、並列ではなく順次に実行する必要があるためです。 (私たちは、タスクは、時には異なるスレッド上で並列に処理され、データベースに保存するときにOptimisticConcurrencyExceptionを取得していることを見ているように、これは最近の要件となっています。)

    内の個々のオブジェクトにロックをかける

辞書は問題ではありませんが、リストをリフレッシュしながら現在ロックされているオブジェクトを辞書から削除しようとすると、新しい問題が発生します。

問題のある手順を順番に実行する小さなコンソールアプリケーションを作成しようとしましたが、 'Monitor.Exit(key);' 'オブジェクト同期メソッドが非同期コードブロックから呼び出されました。'という例外がスローされます。

class Program 
{ 
    static ConcurrentDictionary<object, int> _handles = new ConcurrentDictionary<object, int>(); 

    static void Main(string[] args) 
    { 
     object lockableKey = 'key'; 

     _handles.AddOrUpdate(lockableKey, 10, (oldkey, oldvalue) => 10); 
     var key = _handles.Keys.FirstOrDefault(h => h == lockableKey); 

     Console.WriteLine("key found, beginning to lock"); 
     Monitor.Enter(key); 
     Console.WriteLine("key locked"); 

     ////Console.WriteLine("applying outer lock, hoping for code to stop executing"); 
     ////Monitor.Enter(_handles); 

     Console.WriteLine("Removing key from dictionary"); 
     int dummy; 
     _handles.TryRemove(lockableKey, out dummy); 

     Console.WriteLine("Removing lock from object removed from dictionary"); 
     Monitor.Exit(key); 
    } 

そこで質問は、辞書を更新し、続行する前に実行を完了するために、辞書内のオブジェクト上のすべての子ロックのマスターロックと待機のような役割を果たし、外側辞書にロックを適用するが方法です着信イベントを処理するには?

+0

コードが混乱しています。 'lockableKey'(" key "に設定し、二重引用符で書く)を使用して辞書から検索します(' key'は "key"または 'null'のみを持つことができます)。 'FirstOrDefault')。次に、値 "key"(または例外をスローする 'null')でロックしようとしています。最初の「キー」が辞書内の「キー」に等しいという保証はありません。それらは両方とも同じ値の異なる参照である可能性があります。あなたは辞書から "キー"を取り除いて、ロックを解除しようとしています。なぜ呼び出さないのですか? 'TryRemove(theKey)'を始めるのはどうですか? –

+0

この例は実際に実装されたコードではありませんが、私が想定しているイベントの順序の連続的な例です(別のスレッドで発生します)。「オブジェクト同期メソッドが非同期のコードブロックから呼び出されました」という例外が発生する可能性があることに直面しています。したがって、外側のロックと実際の質問の必要性。 – sebpinski

+0

辞書の更新を1つのスレッドで実行する可能性について考えましたか?単一スレッドのスケジューラを作成し、このスケジューラで 'Task.Factory.StartNew'過負荷を使用して更新タスクを作成するようにしますか? – galenus

答えて

0

皆さん、助けてくれてありがとうございました。以前のコメントが削除されて私が答えを見つけるのを助けてくれたようです。

私は'ReaderWriterLockSlim' classを実装しました。これにより、複数のリーダースレッドが同時に処理できますが、1つのライタースレッドのみが処理されます。したがって、すべての処理スレッドはリーダーロックを取りますが、ディクショナリを更新するスレッドはライターロックを取り出します。ライター・ロックは、すべてのリーダー・ロックが完了するのを待っていますが、すべてのリーダー・ロックよりも優先されます。だから、ライターロックが要求されると、リーダロックはクリティカルセクションに入る前にライターが完了するまで待たなければなりません。

これに加えて、リーダースレッド内では、同じアイテムを同時に処理する2つのスレッドを防ぐために、Monitor.TryLockを辞書の実際のオブジェクトにも適用します。

関連する問題