web-dev-qa-db-ja.com

ConcurrentDictionary GetOrAdd async

GetOrAddConcurrentDictionaryとともにWebサービスのキャッシュとして使用したいのですが。この辞書の非同期バージョンはありますか? GetOrAddはHttpClientを使用してWebリクエストを作成するので、GetOrAddが非同期であったバージョンのディクショナリがあったらいいでしょう。

混乱を解消するために、辞書のコンテンツは、Webサービスへの呼び出しからの応答になります。

ConcurrentDictionary<string, Response> _cache = new ConcurrentDictionary<string, Response>();



var response = _cache.GetOrAdd("id", (x) => { _httpClient.GetAsync(x).GetAwaiter().GetResponse();} )
10
Zeus82

GetOrAddは、辞書の値へのアクセスは長時間実行される操作ではないため、非同期操作にはなりません。

ただし、実行できるのは、マテリアライズされた結果ではなく、単にタスクをディクショナリに格納することです。結果が必要な人は、そのタスクを待つことができます。

ただし、操作が複数回ではなく、一度だけ開始されるようにする必要もあります。一部の操作が複数回ではなく1回だけ実行されるようにするには、Lazyも追加する必要があります。

ConcurrentDictionary<string, Lazy<Task<Response>>> _cache = new ConcurrentDictionary<string, Lazy<Task<Response>>>();

var response = await _cache.GetOrAdd("id", url => new Lazy<Task<Response>>(_httpClient.GetAsync(url))).Value;
9
Servy

GetOrAddメソッドは、この目的で使用するのにそれほど適していません。ファクトリーが1回だけ実行されることは保証されないため、正しいバケットを2回ハッシュして見つける必要がないという点で、ファクトリーの唯一の目的はマイナーな最適化です(とにかく追加はめったにないため、マイナーな最適化です)。 2つの別々の呼び出しで取得および設定します)。

最初にキャッシュを確認し、キャッシュに値が見つからない場合は、何らかのクリティカルセクション(ロック、セマフォなど)を入力し、キャッシュを再確認してください。それでも見つからない場合は、値をフェッチしてください。キャッシュに挿入します。

これにより、バッキングストアが一度だけヒットすることが保証されます。複数の要求が同時にキャッシュミスを受け取った場合でも、最初の要求だけが実際に値をフェッチし、他の要求はセマフォを待って、クリティカルセクションのキャッシュを再チェックするため、早期に戻ります。

擬似コード(非同期で待機できるため、カウント1のSemaphoreSlimを使用):

async Task<TResult> GetAsync(TKey key)
{
    // Try to fetch from catch
    if (cache.TryGetValue(key, out var result)) return result;

    // Get some resource lock here, for example use SemaphoreSlim 
    // which has async wait function:
    await semaphore.WaitAsync();    
    try 
    {
        // Try to fetch from cache again now that we have entered 
        // the critical section
        if (cache.TryGetValue(key, out result)) return result;

        // Fetch data from source (using your HttpClient or whatever), 
        // update your cache and return.
        return cache[key] = await FetchFromSourceAsync(...);
    }
    finally
    {
        semaphore.Release();
    }
}
5
odyss-jii