2016-10-17 8 views
0

private ConcurrentDictionaryは、いくつかのDBキーの単純なルックアップテーブルです。GetOrAddを呼び出すときに、非同期メソッドの結果を.NET ConcurrentDictionaryに格納する方法

私はConcurrentDictionaryを利用して、同じコード行への2回以上のリクエストが同時に行われたときにdbに対して1回の呼び出しを行うようにしています。 (これが私がConcurrentDictionaryを使用している理由です)

どうすればいいですか? ....

private readonly ConcurrentDictionary<string, Task<int>> _myKeys = new ConcurrentDictionary<string, Task<int>>(); 

... 

private async Task<int> DoStuffAsync(string key) 
{ 
    // do stuff here. 

    return await _myKeys.GetOrAdd(key, 
           async k => await _db.GetId(k) 
                .ConfigureAwait(false)) 
         .ConfigureAwait(false); 
} 

任意のアイデア

この

は..私は 行うにをしようとしていたものですが、私はそれは...ない 結果タスクの辞書に Taskを保存すると思います?

編集:

私のメソッドシグネチャと私が返すものに注目してください。 intを返すほうがいいですか?Task<int>ではなく、何とか私のdbコールがまだ非同期であるようにリファクタリングしています。

+0

タスクを辞書に保存すると何が問題になりますか? –

+0

クローズアップまたはダウン投票に投票している人は、Qを改善する理由を説明してください。 –

+1

@StephenClearyこれが良いか悪いか分かりませんでした。結果がちょうどintであるとき、それは格納する_heavy_オブジェクトのように感じました。 –

答えて

0

GetOrAdd does not guarantee that the delegate will be called only once when it's called from multiple threads at the same time with the same value:

あなたが別のスレッドで同時にGetOrAddを呼び出すと、addValueFactoryは複数回呼び出すことができますが、そのキー/値のペアは、すべての呼び出しのための辞書に追加されない場合があります。

これもthe implementationで見ることができます。

public static async Task<TValue> GetOrAddAsync<TKey, TValue>(
    this ConcurrentDictionary<TKey, TValue> dictionary, 
    TKey key, Func<TKey, Task<TValue>> valueFactory) 
{ 
    TValue resultingValue; 
    if (dictionary.TryGetValue(key, out resultingValue)) 
    { 
     return resultingValue; 
    } 
    return dictionary.GetOrAdd(key, await valueFactory(key)); 
} 

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

ので、GetOrAdd()として良い仕事をどうするには、(入力を省略チェック)のような何かを行うことができます

デリゲートを同時に2回コールしないという要件がパフォーマンスの最適化に過ぎない場合は、これで十分です。

コードの正確さのために必要な場合は、GetOrAddでも十分ではなく、追加の同期を使用する必要があります。

+2

私はこれが典型的な場合のOPのコードよりも悪いと思われます:あなたのバージョンでは、タスクが5秒かかるとし、3秒後に別のタスクが同じキーに対して 'GetOrAddAsync'を呼び出したとします。あなたのコードでは、これは新しいタスクを開始することが保証されています。 OPのコードでは、既存のタスクを再利用する可能性が非常に高いです。はい、あなたはOPのコードは何も保証していないが、それは私がOPのバージョンと一緒に行くだろうことをはるかに可能にしていることは間違いありません。 – hvd

+0

@hvdあなたが正しいです。私は 'Task'sを辞書に保存したくないという前提の下で作業していました。 – svick

関連する問題