web-dev-qa-db-ja.com

ConcurrentDictionary.GetOrAdd()は、キーごとに1回だけvalueFactoryMethodを呼び出すことが保証されていますか?

問題:オブジェクトキャッシュを実装する必要があります。キャッシュはスレッドセーフで、オンデマンドで値を入力する必要があります(遅延読み込み)。値はキーによってWebサービス経由で取得されます(操作が遅い)。そのため、操作がアトミックで同期されていると想定して、ConcurrentDictionaryおよびその GetOrAdd() メソッドを使用することにしました。残念ながら、MSDN記事で次のステートメントを見つけました: 方法:ConcurrentDictionaryからアイテムを追加および削除する

また、ConcurrentDictionaryのすべてのメソッドはスレッドセーフですが、すべてのメソッドがアトミックではありません。具体的には、GetOrAddおよびAddOrUpdateです。これらのメソッドに渡されるユーザーデリゲートは、辞書の内部ロックの外部で呼び出されます。

まあそれは残念ですが、それでも私の答えに完全には答えません。

質問:値ファクトリはキーごとに1回だけ呼び出されますか?私の特定のケースでは、同じ値を求めるWebサービスへの複数の要求を生成する同じキーを探している複数のスレッドが可能ですか?

27
f0rt

バリューファクトリはキーごとに1回だけ呼び出されますか?

いいえ、そうではありません。 ドキュメントによると

異なるスレッドでGetOrAddを同時に呼び出すと、valueFactoryが複数回呼び出されることがありますが、キーと値のペアは呼び出されない場合があります呼び出しごとに辞書に追加されます。

19
Yuval Itzchakov

GetOrAddのソースコード を見てみましょう:

public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
{
    if (key == null) throw new ArgumentNullException("key");
    if (valueFactory == null) throw new ArgumentNullException("valueFactory");

    TValue resultingValue;
    if (TryGetValue(key, out resultingValue))
    {
        return resultingValue;
    }
    TryAddInternal(key, valueFactory(key), false, true, out resultingValue);
    return resultingValue;
}

残念ながら、この場合、2つのvalueFactory呼び出しがたまたま並行して実行されても、GetOrAddが2回以上呼び出されないという保証は何もありません。

8