Parallel.ForEach内部ハッシュテーブルを使用していますか?
-
11-09-2019 - |
質問
Iは体内に集中操作を実行Parallel.ForEachループを有している。
の動作は値を格納するハッシュテーブルを使用することができ、そして他の連続ループ項目に再利用することができます。集中的な操作が完了した後、私はハッシュテーブルに追加し、次のループ項目が代わりに再び集中的な操作を実行するので、Hashtableにルックアップし、オブジェクトを再利用することができます。
私はParallel.ForEachを使用していますので、しかし、Hashtable.AddとにContainsKeyを、彼らが並行して実行される可能性がありますと(キー)の呼び出しは、同期が外れて行く引き起こし、危険な問題があります。ロックを導入することperfの問題が発生することがあります。
ここでは、サンプルコードです:
Hashtable myTable = new Hashtable;
Parallel.ForEach(items, (item, loopState) =>
{
// If exists in myTable use it, else add to hashtable
if(myTable.ContainsKey(item.Key))
{
myObj = myTable[item.Key];
}
else
{
myObj = SomeIntensiveOperation();
myTable.Add(item.Key, myObj); // Issue is here : breaks with exc during runtime
}
// Do something with myObj
// some code here
}
このシナリオを扱うことができ、いくつかのAPI、TPLライブラリ内のプロパティの設定、存在する必要があります。ありますか?
解決
あなたは System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>
を探しています。新しい同時コレクションが大幅ロック機構改善使用し、並列アルゴリズムでexcellectly実行する必要があります。
編集:結果は次のようになります。
ConcurrentDictionary<T,K> cache = ...;
Parallel.ForEach(items, (item, loopState) =>
{
K value;
if (!cache.TryGetValue(item.Key, out value))
{
value = SomeIntensiveOperation();
cache.TryAdd(item.Key, value);
}
// Do something with value
} );
警告ののワード:のitems
の要素がすべてではないが、その後item.Key
は、そのキーのために二回呼び出される可能性があり、ユニークなSomeIntensiveOperation
を持っていない場合。例では、キーはSomeIntensiveOperation
に渡されるが、それは必ずしも(「値を持つ何かをする」のコードは、キー/ valueAとキー/ VALUEBのペアを実行することができ、そして唯一つの結果がキャッシュに保存されているになるだろうことを意味していませんいずれかSomeIntensiveOperationによって計算最初のもの)。あなたはそれが問題だ。の場合は、このを処理するために並列怠惰な工場が必要と思います。また、明白な理由のためにSomeIntensiveOperationは、スレッドセーフである必要があります。
他のヒント
ReaderWriterLockを使用し、これは短期のものである読み込ん多く、いくつかの書き込みを持っている仕事のための優れた性能を有しています。あなたの問題は、この仕様に合うようです。
すべての操作はすぐに実行して無料ロックします、書き込みが起こっているとき、誰もがブロックされるだけの時間があり、そしてその書き込みだけであれば、ハッシュテーブルの何かを突き出すのにかかるようです読みます。
私はいくつかのコードを下に投げますね...
ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
Hashtable myTable = new Hashtable();
Parallel.ForEach(items, (item, loopState) =>
{
cacheLock.EnterReadLock();
MyObject myObj = myTable.TryGet(item.Key);
cacheLock.ExitReadLock();
// If the object isn't cached, calculate it and cache it
if(myObj == null)
{
myObj = SomeIntensiveOperation();
cacheLock.EnterWriteLock();
try
{
myTable.Add(item.Key, myObj);
}
finally
{
cacheLock.ExitWriteLock();
}
}
// Do something with myObj
// some code here
}
static object TryGet(this Hashtable table, object key)
{
if(table.Contains(key))
return table[key]
else
return null;
}
私は(多かれ少なかれ明示的)ロックを使用するよりも、他の正しい選択を参照していない(同期Hashtableのはちょうどロックとすべてのメソッドをオーバーライドします)。
別のオプションは、辞書が同期して行くことを許可する可能性があります。競合状態が破損している辞書は、それだけでいくつかの余分な計算を行うためのコードが必要になりますしません。ロックまたは欠落メモ化が悪化効果を持っているかどうかをチェックするためのコードのプロファイルを作成します。