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

«Как вызвать асинхронный метод внутри синхронного метода в C#?» — вопрос из категории Многопоточность, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Вызов асинхронного метода из синхронного контекста — это антипаттерн, которого следует избегать. Лучшее решение — сделать всю цепочку вызовов асинхронной. Если это невозможно, используйте следующие подходы с пониманием их рисков.

1. Task.Run для CPU-bound операций Используйте, если асинхронный метод выполняет интенсивные вычисления, а не I/O.

public void SyncMethod()
{
    // Запускаем задачу в пуле потоков и синхронно ожидаем результат
    Task.Run(() => AsyncCpuBoundOperation()).GetAwaiter().GetResult();
}

private async Task AsyncCpuBoundOperation()
{
    await Task.Run(() => HeavyComputation());
}

2. .GetAwaiter().GetResult() для I/O операций (с осторожностью) Может привести к взаимоблокировкам (deadlock), особенно в контекстах с SynchronizationContext (например, UI-поток в WPF/WinForms или HttpContext в старых версиях ASP.NET).

public void SyncMethod()
{
    // Риск дедлока! Не используйте в UI-потоке или ASP.NET (не Core).
    AsyncIoMethod().GetAwaiter().GetResult();
}

3. Запуск без ожидания (Fire-and-forget) Подходит только для фоновых операций, результат которых не нужен вызывающему коду. Опасен потерей исключений.

public void SyncMethod()
{
    // Просто запускаем задачу. Исключения могут быть не обработаны.
    _ = AsyncMethod();
}

Ключевые рекомендации:

  • Избегайте Task.Wait() и Task.Result — они оборачивают исключения в AggregateException и также подвержены дедлокам.
  • В ASP.NET Core используйте асинхронные контроллеры и методы (async Task<IActionResult>). Синхронные обертки нарушают масштабируемость.
  • В библиотечном коде предоставляйте как асинхронные (DoWorkAsync), так и синхронные (DoWork) версии методов, если это необходимо, где синхронная версия является оберткой.
  • Рассмотрите IAsyncDisposable и IAsyncEnumerable<T> для полной асинхронной интеграции.