Синхронный вызов асинхронного метода

Мне нужно синхронно вызвать асинхронный метод по независящим от меня причинам. Я разрабатываю библиотеку, которая использует другую библиотеку, работающую асинхронно, и мне нужно использовать ее из реализации класса Stream. Такой класс содержит синхронные и асинхронные методы, и меня беспокоят синхронные:

public override sealed int Read(byte[] buffer, int offset, int count)
{ 
    // potential deadlock on single threaded synchronization context
    return ReadAsync(buffer, offset, count).Result;
}

public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
   return await _lib.ReadAsync(buffer,offset,count);
}

Я читал Как мне запустить асинхронную задачу‹T› метод синхронно?, в частности этот ответ, а в комментариях @Stephen Cleary заявляет, что решение не хорошо, так как некоторые части ASP.NET ожидают AspNetSynchronizationContext. Я разрабатываю библиотеку, поэтому не знаю, откуда будут вызываться мои классы.

Каким будет самый безопасный способ синхронного вызова асинхронного метода?


person vtortola    schedule 24.02.2014    source источник


Ответы (1)


Стивен Тоуб описывает все различные подходы с соответствующими недостатками в своем блоге.

Есть только два решения общего назначения, ни одно из которых не идеально:

  1. Дублируйте логику в вашем слое; ваши синхронные методы вызывают синхронные методы в библиотеке. (Это предполагает, что библиотека имеет как синхронные, так и асинхронные методы).
  2. Заблокируйте возвращенную задачу, как в вашем примере кода. Это предполагает, что библиотека всегда использует ConfigureAwait(false), что находится вне вашего контроля. Если библиотека не зависит от контекста, то может быть безопаснее бросить асинхронный вызов в Task.Run и вместо этого заблокировать эту задачу. Кроме того, убедитесь, что вы разворачиваете исключения при блокировке задачи, поскольку Wait или Result будут заключать исключения в AggregateException.

Оба этих решения имеют серьезные проблемы с обслуживанием.

person Stephen Cleary    schedule 24.02.2014
comment
Я что-то упускаю? Task<T>.Wait будет блокироваться независимо от политик продолжения. - person Gusdor; 24.02.2014
comment
@Gusdor: Подобная блокировка простой способ вызвать взаимоблокировки, как я объясняю в своем блоге. - person Stephen Cleary; 24.02.2014
comment
Туше! Я что-то упустил. Спасибо за объяснение. - person Gusdor; 24.02.2014
comment
Кажется, что первое — единственно разумное и безопасное решение. Подход Task.Run был бы хорошим временным решением, пока я не запланирую время для написания синхронных методов. - person vtortola; 24.02.2014
comment
@vtortola: обычно я рекомендую первый, но ни один из них не идеален. - person Stephen Cleary; 24.02.2014