Имеется некий асинхронный метод:
public async Task<Foo> GetFooAsync();
мне также нужен его синхронный аналог
public Foo GetFoo();
Мне не очень хотелось бы полностью переписывать GetFooAsync
и создавать на его основе синхронную версию метода. Достаточно ли будет сделать например так?:
public Foo GetFoo()
{
return GetFooAsync().GetAwaiter().GetResult();
}
или у такой реализации "в лоб" есть подводные камни и так лучше не делать?
Обратите внимание на вот этот вопрос: Зависает оператор `await` в оконном приложении / программа висит при вызове Task.Result или Wait
Там показана основная проблема таких вот вызовов GetResult()
. И там же есть решение проблемы "но что делать если очень надо" через временный контекст синхронизации:
public Foo GetFoo()
{
using (var ctx = new QueueSynchronizationContext())
{
var task = GetFooAsync();
ctx.WaitFor(task);
return task.GetAwaiter().GetResult();
}
}
Но это решение подходит только для однопоточных контекстов синхронизации, при выполнении в неограниченном пуле потоков правильнее будет вызвать task.GetAwaiter().GetResult();
напрямую. В ограниченных же пулах потоков все еще сложнее - нельзя использовать QueueSynchronizationContext
потому что он убьет параллельность - но нельзя и использовать блокирующее ожидание, потому что оно займет один поток.
Универсальным способом будет Task.Run(() => GetFooAsync()).GetAwaiter().GetResult()
, но этот способ смотрится странно (и опять-таки, несет проблемы когда пул потоков ограничен).
Наверное, проще всего просто выбрать только один метод и использовать только его. Но если вы пишите библиотеку - возможно, правильнее всего будет написать алгоритм два раза, и в блокирующем варианте, и в асинхронном.
В общем случае нельзя просто взять и вызывать GetResult()
/ .Result
в асинхронном коде, т.к. это с большой вероятностью приведет к дедлоку:
Возможные варианты обхода дедлока:
.ConfigureAwait(false)
на всех асинхронных вызовах внутри вашего асинхронного метода (и на вызовах внутри вызовов из вашего метода). Тогда продолжнения для кода внутри метода будут выполняться на потоках из пула, без сохранения контекста.string code = Task.Run(GetFooAsync).Result
;Но лучше так не делать, и тянуть асинхронность до самого верха.
Так в лоб лучше не делать. И я настоятельно рекомендовал переписать отдельно на синхронный метод. Но уж если слишком лень и точно известно, что таск не уйдет в дэдлок, то можно попробовать так.
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
Но это не панацея, слишком много если в таком случае. Проще переписать синхронный отдельно.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Здравствуйте, в самом конструкторе Visual Studio, в открытом окне когда наводишь курсор на вкладку (X) закрыть, данный крестик выделяется, крестик...
Вообщем есть такая проблема, те
Доброго времени сутокЕсть WPF-приложение + дампер(MiniDumpWriteDump)