35

のドキュメントには状態が明示されていないため、デリゲートとその実行が同期さConcurrentDictionaryれることは期待できません(それぞれGetOrAdd()およびAddOrUpdate()操作から)。valueFactoryupdateValueFactory

したがって、独自の並行制御を手動で実装せずに、並行制御を必要とするリソースの使用を実装することはできないと思います。おそらく[MethodImpl(MethodImplOptions.Synchronized)]、デリゲートを使用するだけです。

私は正しいですか?または、スレッドセーフであるという事実ConcurrentDictionaryは、これらのデリゲートへの呼び出しが自動的に同期されることを期待できますか(スレッドセーフも)?

4

2 に答える 2

36

はい、その通りです。ユーザーデリゲートはによって同期されていませんConcurrentDictionary。それらを同期させる必要がある場合、それはあなたの責任です。

MSDN自体は次のように述べています。

また、ConcurrentDictionaryのすべてのメソッドはスレッドセーフですが、すべてのメソッド、特にGetOrAddとAddOrUpdateがアトミックであるとは限りません。これらのメソッドに渡されるユーザーデリゲートは、ディクショナリの内部ロックの外部で呼び出されます。(これは、不明なコードがすべてのスレッドをブロックするのを防ぐために行われます。)

「方法:ConcurrentDictionaryからアイテムを追加および削除する」を参照してください。

これは、ConcurrentDictionary提供するデリゲートが何を実行するか、またはそのパフォーマンスがわからないためです。そのため、デリゲートをロックしようとすると、パフォーマンスに悪影響を及ぼし、ConcurrentDictionaryの値を台無しにする可能性があります。

したがって、必要に応じてデリゲートを同期するのはユーザーの責任です。上記のMSDNリンクには、実際には、それが行う保証と行わない保証の良い例があります。

于 2012-05-07T17:48:32.033 に答える
27

これらのデリゲートは同期されていないだけでなく、一度だけ発生することも保証されていません。実際、これらはへの呼び出しごとに複数回実行できますAddOrUpdate

たとえば、のアルゴリズムは次のAddOrUpdateようになります。

TValue value;
do
{
  if (!TryGetValue(...))
  {
    value = addValueFactory(key);
    if (!TryAddInternal(...))
    {
      continue;
    }
    return value;
  }
  value = updateValueFactory(key);
} 
while (!TryUpdate(...))
return value;

ここで2つのことに注意してください。

  • デリゲートの実行を同期するための努力はありません。
  • デリゲートはループで呼び出されるため、複数回実行される可能性があります。

したがって、2つのことを確実に行う必要があります。

  • デリゲートに独自の同期を提供します。
  • デリゲートに、実行回数に依存する副作用がないことを確認してください。
于 2012-05-07T19:34:14.360 に答える