мое использование асинхронного лямбда правильно в шаблоне кэша в стороне? - PullRequest
0 голосов
/ 16 июня 2019

Я немного запутался с асинхронной и лямбда-функцией.

У меня есть старая функция CacheHelper (в основном шаблон в стороне от кэша), которая делает это:

public static T GetOrAdd<T>(Func<Task<T>> builder, TimeSpan expiresIn, bool ignoreNullValues, params string[] keys)
{
      ////check if the cache item is available
       xxxx
      //// if not, call the builder function to get fresh
      var item = builder().Result;
      //// add to cache and return the item
      return item;
}

Теперь мы переходим к асинхронному шаблону, поэтому я вызываю помощника кеша:

    CacheAsideHelper.GetOrAdd(
              async () => await _currencyRepository.GetCurrencyInfo(currencyCode, commandTimeout, taskTimeout, _trackingId),
                    new TimeSpan(Constants.ExpirationDays, 0, 0), true, key);

Я запускаю несколько тестов и, кажется, результат ожидаемый. Но один из моих коллег сказал, что, поскольку я передаю асинхронную лямду, мой кеш иногда может содержать Task<T>, отличный от объекта T. Так что мне как-то нужно его подождать.

Однако мой тест, похоже, дает правильные данные, в том числе и в моем вспомогательном коде Cache, как у меня

var item = builder().Result;

Кэш всегда будет содержать реальные данные (кроме Task<T>). Я прав или мой коллега прав?

1 Ответ

0 голосов
/ 19 июля 2019

На самом деле это нормально для кеширования Задача сама не результат, см. Здесь https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/

Задание как класс очень гибкое и дает в результате преимущества. За Например, вы можете ждать его несколько раз, любым количеством потребителей одновременно. Вы можете сохранить один в словаре для любого количества Последующие потребители ожидают в будущем, что позволяет ему быть используется в качестве кэша для асинхронных результатов.

Имея задание (выполненное или нет), вы можете ждать его столько раз, сколько захотите. Таким образом, вы можете сделать что-то вроде этого:

public static Task<T> GetOrAdd<T>(Func<Task<T>> builder, string key, TimeSpan expiresIn)
{
      //1. try find task in cache
      //2. if task is failed or cancelled - run builder and put task into cache
      //3. otherwise just return the task
}

Есть еще две проблемы:

  1. обработка исключений в builder()
  2. одновременная инициализация: все еще возможно несколько раз запускать builder() из разных потоков

Здесь вы можете найти возможные способы решения обеих проблем: https://github.com/MaximTkachenko/cache-once/blob/master/src/Mtk.CacheOnce/MemoryCacheOnceExtensions.cs

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...