私は、外部辞書の操作がコレクション全体への操作をロックするネストされた辞書へのアクセスを同期するための安全なアプローチを見つけようとしています。しかし、内部辞書が取り出されると、外部ロックを解放し、スレッドが内部辞書を操作しないようにしたい。辞書をロックして、安全に辞書の値にロックを渡す方法は?
内側の辞書が外側の辞書内に存在すると、これにはlock
キーワードを使用するのは簡単ですが、競合条件を導入せずに内側の辞書を追加して設定するという安全なアプローチを見つけるのは苦労しています。内側の辞書を "取り込む"ことは高価な操作であると仮定します。このため、外部辞書のlock
の下に置くのはオプションではありません。他のスレッドは、他の内部辞書に対する操作を実行するために外部辞書にアクセスしなければならず、一方、「内部」は新しい内部辞書に対して実行される。
私は私の質問をよりよく説明する以下の例を挙げました。私は競合状態
- 些細なアプローチは、しかし、不必要に複数の内部辞書を取り込むにつながることができます。これは、レースで勝利した最初のインナー辞書を使用して終了します。
- 私は考えているアプローチは、しかし、それは不慣れな領域です。広範なテストをしても、C#の
Monitor
オブジェクトを使用した私の経験の不足が予期せぬ結果を招く恐れがあります。
例1:私の問題は安全ではないと考えています。しかし、これは伝統的に同期しています。このアプローチを使用することをお勧めしますが、lock
キーワードを使用して必要な動作を達成する方法がわかりません。
public void SyncronizationExample1(Dictionary<TKey, Dictionary<TKey2, ReferenceTypedValues>> outerDictionary, TKey newKey)
{
Dictionary<TKey2, ReferenceTypedValues> innerDictionary = null;
lock (outerDictionary)
{
// No need to add a new innerDictionary, it already exists
if (outerDictionary.ContainsKey(newKey))
{
return;
}
innerDictionary = new Dictionary<TKey2, ReferenceTypedValues>();
outerDictionary.Add(newKey, innerDictionary);
}
// I want to allow other threads to have access to outerDictionary
// However, I don't want other threads working against THIS innerDictionary
lock (innerDictionary)
{
// Here lies my concern with this approach. Another thread could have
// taken the lock for innerDictionary. Doing this all under the lock
// for outerDictionary would be safe but would prevent access to other
// inner dictionaries while expensive operations are performed only
// pertaining to THIS innerDictionary
this.PopulateInnerDictionary(innerDictionary);
}
}
例2:複数のスレッドが同時に操作を試みた場合、不要な計算として理想的ではない、実施例1に描か問題が発生しないlock
を用いてアプローチが起こり得ます。ただし、グローバルな "Populate"ロックに対してロックするようにこのアプローチを変更することができます。これにより、複数のスレッドが同時に異なる内部ディメンションに値を設定することを防ぐことができます。
public void SyncronizationExample3(Dictionary<TKey, Dictionary<TKey2, ReferenceTypedValues>> outerDictionary, TKey newKey)
{
lock (outerDictionary)
{
if (outerDictionary.ContainsKey(newKey))
{
// No need to add a new innerDictionary, it already exists
return;
}
}
var innerDictionary = new Dictionary<TKey2, ReferenceTypedValues>();
// Expensive operation - if called by multiple threads at the same time
// multiple innerDictionaries will be populated but only the one to win
// the race will be utilized.
this.PopulateInnerDictionary(innerDictionary);
lock (this.outerDictionary)
{
if (!outerDictionary.ContainsKey(newKey))
{
// If another thread won the race this newKey would be in outerDictionary
// The innerDictionary we just populated is redundant
outerDictionary.Add(newKey, innerDictionary);
}
}
}
例3:私は、実施例1に示された潜在的な同期の問題に対する解決策であると信じることはしかし、私はMonitor
パターンを使用してに不慣れだと大幅にフィードバックをいただければ幸いです。
public void SyncronizationExample3(Dictionary<TKey, Dictionary<TKey2, ReferenceTypedValues>> outerDictionary, TKey newKey)
{
Dictionary<TKey2, ReferenceTypedValues> innerDictionary = null;
bool aquiredLockForOuterDictionary = false;
bool aquiredLockForInnerDictionary = false;
try
{
Monitor.Enter(outerDictionary, ref aquiredLockForOuterDictionary);
if (outerDictionary.Contains(newKey)
{
// No need to add a new innerDictionary, it already exists
return;
}
innerDictionary = new Dictionary<TKey2, ReferenceTypedValues>();
outerDictionary.Add(newKey, innerDictionary);
// This is where I "handoff" the lock to innerDictionary to alleviate my concern
// in Example 1 where another thread could steal the innerDictionary lock
Monitor.Enter(innerDictionary, ref aquiredLockForInnerDictionary);
}
finally
{
// I read that this bool pattern was preferred for .net 4+,
// however I am unsure if this is the best practice
if (aquiredLockForOuterDictionary)
{
Monitor.Exit(dictionary);
}
}
try
{
if (!aquiredLockForInnerDictionary)
{
// An exception must have occurred prior to or during the acquisition
// of this lock. Not sure how I'd handle this yet but
// I'm pretty shit out of luck.
return;
}
// Here I would perform an expensive operation against the innerDictionary
// I do not want to lock consumers form accessing other innerDictionaries
// while this computation is done.
this.PopulateInnerDictionary(innerDictionary);
}
finally
{
// I need to check this here incase an exception in the first
// try finally prevented this from being acquired
if (aquiredLockForInnerDictionary)
{
Monitor.Exit(innerDictionary);
}
}
}
おそらく 'ConcurrentDictionary <>'を使用していますか? – MickyD