Как сохранить результат асинхронного метода в .NET ConcurrentDictionary при вызове GetOrAdd?

У меня private ConcurrentDictionaryесть простая таблица поиска некоторых ключей БД.

Я пытаюсь использовать ConcurrentDictionaryтак, чтобы он выполнял только один вызов в db, когда 2+ запросы к одной строке кода выполняются одновременно. (Вот почему я использую ConcurrentDictionary.)

Как я могу это сделать, пожалуйста?

Это то, что я пытался сделать .. но я думаю, что он хранит Taskв словаре ... не результат задачи ....

частные чтения ConcurrentDictionary < строка , Task <Int> > _myKeys = новый ConcurrentDictionary < строка , Task <Int> > ();      

...

private async Задача <int> DoStuffAsync ( строковый ключ ) { // Здесь что-то делать. 

   

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

Есть идеи?

РЕДАКТИРОВАТЬ:

Обратите внимание на мою подпись метода и то, что я возвращаю. Лучше ли возвращать a, intа не a, Task<int>а затем каким-то образом рефакторировать мой вызов db, чтобы все еще быть асинхронным .. но ... лучше?

c#,async-await,concurrentdictionary,

1

Ответов: 1


1

GetOrAdd не гарантирует, что делегат будет вызываться только один раз, когда он вызывается из нескольких потоков одновременно с одним и тем же значением:

Если вы звоните TValue resultingValue ; если ( TryGetValue ( ключ , из resultingValue )) { вернуться resultingValue ; } TryAddInternal ( ключ , valueFactory ( ключ ), ложно , правда , из resultingValue ); return resultValue ; одновременно на разных потоках addValueFactory может вызываться несколько раз, но его пара ключ / значение не может быть добавлена ??в словарь для каждого вызова.

Это можно увидеть и в реализации :

GetOrAdd()

Итак, чтобы справиться с такой же хорошей работой , вы можете сделать что-то вроде (проверка ввода опущена):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)); }

GetOrAdd

Если требование не вызывать делегата дважды в то же время, это просто оптимизация производительности, этого должно быть достаточно.

Если это необходимо для правильности вашего кода, то даже GetOrAddэтого недостаточно, и вам потребуется использовать дополнительную синхронизацию.

C #, асинхронному Await, concurrentdictionary,