Может ли метод .NET ConcurrentDictionary GetOrAdd вызывать асинхронный метод?

Я рассматриваю использование ConcurrentDictionary для хранения некоторых кэшированных данных, которые поступают из медленного источника (например, из базы данных).

Обращение к базе данных async. Итак, возможно ли, чтобы параллельный словарь вызывал асинхронный метод, если элемент не существует в словаре?

Например:

const int userId = 1;
var cachedUsers = new ConcurrentDictionary<int, Task<User>>();
var user = await cachedUsers.GetOrAdd(userId, val => GetSlowDbResultAsync(userId, cancellationToken));

так что этот псевдокод выше пытается сделать, это сказать:

  • существует ли пользователь № 1 в C-Dict?
  • Да, хорошо, используйте это.
  • Нет, возьмите пользователя №1 из БД и вставьте его в кеш.

Итак, поместив Task<User> в «значение» параллельного словаря (для ключа/значения):

  • это нормально?
  • является ли код, который я написал выше, приемлемым использованием этого или я просто злоупотребил всем, что священно с помощью async/await/c#

Примечания:

Этот вопрос навеян моим вопросом в Твиттере.
Это похоже на предыдущий вопрос, который я задал , но не получил большой поддержки.


person Pure.Krome    schedule 31.10.2017    source источник
comment
Если вы завернете это в метод GetAsync (чтобы клиентам не нужно было понимать реализацию), у вас все будет хорошо, я думаю. При первом попадании вы будете ожидать вызова базы данных, а при втором (для того же идентификатора пользователя) — завершенной задачи. Мне кажется, это нормально.   -  person Llama    schedule 31.10.2017
comment
Почему вы выбрали ConcurrentDictionary? Надеюсь, вы не используете асинхронность, надеясь, что это ускорит работу.   -  person CodingYoshi    schedule 31.10.2017
comment
См. отмеченный дубликат. В его ответе используется именно та техника, о которой вы спрашиваете. Он отлично работает, если предположить, что остальная часть вашего кода написана для него.   -  person Peter Duniho    schedule 31.10.2017


Ответы (1)


Как насчет того, чтобы попытаться написать метод расширения, что-то вроде:

class Extension
{
    public static User Get(this ConcurrentDictionary<int, User> conDict, int userID)
    {
        User user = null;
        if (!conDict.TryGetValue(userID, out user))
        {
            user = GetSlowDbResultAsync(userId, cancellationToken)
           conDict.TryAdd(userID, user);
        }

        return user;
    }
}

дополнительную информацию о методе расширения см. в этом статья.

person Sean    schedule 31.10.2017
comment
Как это отвечает на вопрос? - person CodingYoshi; 31.10.2017
comment
@sean, что произойдет, если два запроса ОБА делают TryGetValue одновременно, оба возвращают false .... :) Затем мы делаем 2x медленные вызовы БД, дальше? - person Pure.Krome; 31.10.2017