В C# код, использующий асинхронность, должен следовать принципу: "async всё", то есть если есть асинхронный метод, его нужно вызывать в асинхронном контексте. Однако бывает возникают ситуации, когда нужно вызвать асинхронный метод из синхронного кода (например, в старом коде, в desktop UI-приложениях).
Существуют несколько подходов для таких вызовов, рассмотрим основные из них:
1. Task.Result и Task.Wait()
Самый простой способ — использовать свойство
Result или метод Wait(), чтобы блокировать выполнение до завершения асинхронной задачи.public int GetCountMethod()
{
var task = GetCountMethodAsync();
task.Wait()
return task.Result;
}
или
public int GetCountMethod()
{
var task = GetCountMethodAsync();
return task.Result;
}
Task.Wait() метод блокирует поток до тех пор, пока не taskбудет успешно выполнен. Task.Result также блокирует вызывающий поток, пока результат не готов, как только задача будет завершена, возвращается результат.Основной проблемой данного способа является возможный deadlock, который может произойти, если вызывающий поток ожидает завершения асинхронной задачи, которая пытается вернуться на основной поток для выполнения продолжения из-за контекста синхронизации, что может произойти в UI-приложениях.
Еще одним минусом является то, что в случае возникновения исключения оно будет обернуто в
AggregateException, который придется раскручивать для получения действительного исключения.2. GetAwaiter().GetResult()
Данный способ аналогичен предыдущему, но будет более предпочтительнее, т.к. не оборачивает возникающие исключения в
AggregateException.public int GetCountMethod()
{
var result = GetCountMethodAsync().GetAwaiter().GetResult();
return result;
}
Однако такой подход также может привести к deadlock'ам
3. Task.Run()
Можно использовать
Task.Run() для выполнения асинхронного кода в отдельном фоновом потоке, что решает проблему deadlock.public int GetCountMethod()
{
var result = Task.Run(async () => await GetCountMethodAsync()).GetAwaiter().GetResult();
return result;
}
4. async Main
А для того, чтобы не использовать весь этот цирк, если планируем использовать асинхронный код, то делаем асинхронную точку входа.