スレッドセーフのニーズConcurrentDictionary
をすべて満たす場合、これは優れたオプションです。そうでない場合、つまり、少し複雑なことをしている場合は、通常のDictionary
+ lock
の方が良いオプションです。たとえば、いくつかの注文を辞書に追加していて、注文の合計金額を更新し続けたいとします。次のようなコードを書くことができます:
private ConcurrentDictionary<int, Order> _dict;
private Decimal _totalAmount = 0;
while (await enumerator.MoveNextAsync())
{
Order order = enumerator.Current;
_dict.TryAdd(order.Id, order);
_totalAmount += order.Amount;
}
このコードはスレッドセーフではありません。複数のスレッドが_totalAmount
フィールドを更新すると、フィールドが破損した状態になる可能性があります。だからあなたはそれでそれを保護しようとするかもしれませんlock
:
_dict.TryAdd(order.Id, order);
lock (_locker) _totalAmount += order.Amount;
このコードは「安全」ですが、スレッドセーフではありません。が_totalAmount
ディクショナリのエントリと一致しているという保証はありません。別のスレッドがこれらの値を読み取って、UI要素を更新しようとする場合があります。
Decimal totalAmount;
lock (_locker) totalAmount = _totalAmount;
UpdateUI(_dict.Count, totalAmount);
totalAmount
辞書に注文の数に対応しない場合があります。表示された統計情報が間違っている可能性があります。この時点lock
で、ディクショナリの更新を含めるように保護を拡張する必要があることがわかります。
// Thread A
lock (_locker)
{
_dict.TryAdd(order.Id, order);
_totalAmount += order.Amount;
}
// Thread B
int ordersCount;
Decimal totalAmount;
lock (_locker)
{
ordersCount = _dict.Count;
totalAmount = _totalAmount;
}
UpdateUI(ordersCount, totalAmount);
このコードは完全に安全ですが、aを使用することの利点はすべてConcurrentDictionary
なくなりました。
Dictionary
内部の内部ロックConcurrentDictionary
が無駄になり、冗長になるため、通常のを使用するよりもパフォーマンスが低下します。
- シェア変数の保護されていない使用については、すべてのコードを確認する必要があります。
- ぎこちないAPI(
TryAdd
?、AddOrUpdate
?)の使用に悩まされています。
だから私のアドバイスは:Dictionary
+ lock
から始めて、ConcurrentDictionary
このオプションが実際に実行可能な場合は、パフォーマンスの最適化として後でaにアップグレードするオプションを保持します。多くの場合、そうではありません。