Ответ
Краткий ответ: Это зависит от контекста синхронизации (SynchronizationContext) и использования ConfigureAwait(). По умолчанию в консольном приложении или веб-сервисе (ASP.NET Core) — да, на ThreadPool. В приложениях с UI (WPF, WinForms) — нет, продолжение вернется в UI-поток.
Детали:
-
Контекст синхронизации (SynchronizationContext): Это "диспетчер", который решает, где выполнить продолжение.
- UI-приложения: Имеют контекст, который маршалирует продолжение обратно в главный UI-поток.
- ASP.NET Core и Консольные приложения: Не имеют специального контекста (
SynchronizationContext.Currentравенnull). Продолжение выполняется на любом доступном потоке из ThreadPool.
-
Метод
ConfigureAwait(bool continueOnCapturedContext): Позволяет явно управлять этим поведением.ConfigureAwait(true)или отсутствие вызова (по умолчанию): Захватывает текущий контекст (если он есть).ConfigureAwait(false): Игнорирует контекст. Продолжение всегда выполняется на потоке из ThreadPool.
Практические примеры:
// Пример 1: UI-приложение (WPF/WinForms) - ПЛОХО для фоновой работы
private async void Button_Click(object sender, EventArgs e)
{
// Этот вызов выполнится на фоновом потоке (из ThreadPool)
await File.DeleteAsync("largefile.zip");
// А ЭТОТ КОД вернется в UI-поток (захваченный контекст).
// Это может заблокировать UI, если здесь тяжелая обработка.
ProcessResult(); // Выполняется в UI-потоке
}
// Пример 2: UI-приложение - ХОРОШО для фоновой работы
private async void Button_Click(object sender, EventArgs e)
{
await File.DeleteAsync("largefile.zip").ConfigureAwait(false);
// Теперь этот код выполнится на потоке из ThreadPool.
ProcessResult(); // Выполняется на ThreadPool, не блокируя UI
// Если нужно обновить UI, используйте Dispatcher.Invoke
Dispatcher.Invoke(() => StatusLabel.Text = "Готово");
}
// Пример 3: Веб-API (ASP.NET Core) или консольное приложение
public async Task<IActionResult> DownloadFile()
{
await File.DeleteAsync("temp.txt"); // Контекста нет
// Продолжение выполнится на случайном потоке ThreadPool.
// ConfigureAwait(false) здесь часто избыточен, но не повредит.
return Ok();
}
Рекомендация: В библиотечном коде всегда используйте ConfigureAwait(false), если вам не требуется возвращаться в исходный контекст. Это предотвращает deadlock'и и улучшает производительность.