Вызов asyn c метода с `.Result` в выражении Linq вызывает тупик - PullRequest
1 голос
/ 05 марта 2020

Мы столкнулись с ошибкой, из-за которой нам пришлось проверять список объектов с помощью асинхронного c метода. Автор кода хотел просто вставить его в выражение Linq следующим образом:

var invalidObjects = list
      .Where(x => _service.IsValidAsync(x).Result)
      .ToList();

Метод проверки выглядел примерно так:

public async Task<bool> IsValidAsync(object @object) {
  var validObjects = await _cache.GetAsync<List<object>>("ValidObjectsCacheKey");

  return validObjects.Contains(@object);
}

Это небольшое решение вызвало все приложение повесить на линию await _cache.GetAsync. Кеш это распределённый кеш (redis). После изменения linq на простой foreach и надлежащего ожидания _service.IsValidAsync код заработал без тупиков и практически мгновенно.

На базовом c уровне я понимаю, как работает async-await, но не могу понять, почему это произошло, особенно потому, что в списке был только один объект.

Любое предложение Добро пожаловать!

РЕДАКТИРОВАТЬ: приложение работает на. net Core 2.2, но библиотека, в которой возникла проблема, ориентирована на .netstandard 2.0. System.Threading.SynchronizationContext.Current возвращает ноль во время тупика

EDIT2: Оказывается, смена поставщика кэша (но все еще доступ к нему с помощью асинхронного c метода) также решает проблему, поэтому ошибка может быть клиент кэша Redis: https://github.com/aspnet/Caching/blob/master/src/Microsoft.Extensions.Caching.StackExchangeRedis/RedisCache.cs

1 Ответ

1 голос
/ 05 марта 2020

Помимо уже заявленной проблемы смешивания асинхронных c -wait и блокировки вызовов, таких как .Result или .Wait()

Ссылка Асинхронное / ожидание - Лучшие практики асинхронного программирования

Чтобы суммировать это второе руководство, вам следует избегать смешивания asyn c и кода блокировки. Смешанная асинхронная c и блокировка кода могут вызвать взаимные блокировки, более сложную обработку ошибок и неожиданную блокировку потоков контекста. Исключением из этого правила является метод Main для консольных приложений или, если вы опытный пользователь, управляющий частично асинхронной кодовой базой.

Иногда простой подход, как вы уже обнаружили, заключается в просмотреть список и правильно дождаться асинхронной функции

Например

var invalidObjects = //...

foreach(var x in list){
    if(!(await _service.IsValidAsync(x)))
        invalidObjects.Add(x);
}
...