Что такое ConfigureAwait в C#?

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

Ответ

ConfigureAwait(bool continueOnCapturedContext) — это метод, который настраивает поведение await в отношении контекста синхронизации.

  • ConfigureAwait(true) (значение по умолчанию): После завершения асинхронной операции выполнение продолжается в том же контексте синхронизации, который был захвачен (например, в UI-потоке WPF/WinForms).
  • ConfigureAwait(false): Указывает, что продолжение может выполняться в любом потоке из пула потоков, без привязки к исходному контексту.

Зачем это нужно?

  1. Избежание взаимоблокировок (Deadlocks): В библиотечном коде, если вы заблокируете поток, ожидающий завершения задачи с ConfigureAwait(true), может возникнуть deadlock.
  2. Повышение производительности: Избегание лишних переключений контекста (особенно в UI) и более эффективное использование потоков из пула.

Примеры использования

// Пример для библиотечного кода (рекомендуется ConfigureAwait(false))
public async Task<string> FetchDataFromApiAsync(string url)
{
    using var httpClient = new HttpClient();
    // Продолжение выполнится в потоке из пула, не пытаясь вернуться в UI-поток
    var response = await httpClient.GetStringAsync(url).ConfigureAwait(false);
    return ProcessData(response); // Этот метод также выполнится в потоке из пула
}

// Пример для UI-приложения
private async void Button_Click(object sender, EventArgs e)
{
    var data = await _apiService.FetchDataAsync(); // Без ConfigureAwait(false) здесь
    // Здесь важно вернуться в UI-поток для обновления элементов управления
    textBox.Text = data; // Это сработает корректно
}

Правило

  • В библиотечном коде (классы, не зависящие от UI) всегда используйте ConfigureAwait(false), если вам не нужен исходный контекст.
  • В коде уровня приложения (например, обработчики событий в UI) обычно используйте поведение по умолчанию (ConfigureAwait(true)), чтобы безопасно обновлять элементы интерфейса.