Какова лучшая практика для асинхронного программирования в ASP.NET MVC?

Согласно этой статье ASP.NET требует использования того же SynchronizationContext для асинхронных операций в контроллере, иначе он блокирует работающий поток. В заключение автор упомянул, что мы должны использовать оба метода как лучший метод для предотвращения взаимоблокировок потоков:

  1. В вашей "библиотеке" асинхронных методов используйте ConfigureAwait(false), где это возможно.
  2. Не блокируйте задачи; использовать асинхронный режим до конца.

    Примечание. Лучше всего применять обе передовые практики. Любой из них предотвратит тупик, но оба должны применяться для достижения максимальной производительности и скорости реагирования.

Но с какой целью Microsoft использовала то же самое? SynchronizationContext? Может быть использование ConfigureAwait(false), который отключает такое же ограничение контекста синхронизации, может привести, например, к непредсказуемому поведению или проблемам с производительностью. Таким образом, действительно ли хорошей практикой является использование ConfigureAwait(false) где это возможно?

2 ответа

Решение

ASP.NET требует использования того же SynchronizationContext для асинхронных операций в контроллере, в противном случае он блокирует работающий поток.

Я не совсем уверен, что означает это утверждение. ASP.NET не требует "тот же" контекст синхронизации. Для того, чтобы вы были внутри HttpContextтебе это нужно SynchronizationContext,

Но с какой целью Microsoft использовала то же самое? SynchronizationContext?

Я предлагаю вам прочитать "Все о SynchronizationContext", чтобы получить более полное представление о контекстах синхронизации. По сути, это механизм, который позволяет вам публиковать продолжения в определенной теме. Это может быть использовано, например, для перенаправления работы обратно в поток цикла сообщений пользовательского интерфейса внутри приложений пользовательского интерфейса.

Возможно, использование ConfigureAwait(false), которое отключает такое же ограничение контекста синхронизации, может привести, например, к непредсказуемому поведению или проблемам с производительностью.

Напротив. У маршалинговой работы над контентом синхронизации действительно есть (очень минимальные) накладные расходы, поскольку запрос должен быть опубликован (с использованием абстрактного SynchronizationContext.Post) обратно на нужный поток и контекст. Используя ConfigureAwait(false)Вы экономите это время и просто продолжаете выполнение, на котором когда-либо был выделен поток.

Таким образом, действительно ли хорошей практикой является использование ConfigureAwait (false) везде, где это возможно?

Основная причина сделать это, чтобы избежать тупиков. Когда кто-то звонит Task.Result или же Task.Wait вместо асинхронного ожидания с использованием await, вы получаете классический сценарий взаимоблокировки, когда контекст синхронизации пытается отправить продолжение обратно в поток, но в настоящее время оно заблокировано, потому что Result а также Wait блокируют звонки. Другая причина - незначительные потери производительности, которые вы получаете.

Редактировать:

Почему Microsoft не инкапсулирует этот метод в классе Task? Похоже, у ConfigureAwait (false) есть только только плюсы. Есть ли минусы?

Давайте представим следующий сценарий: вы выполняете метод, который возвращает Task и ожидается использование awaitи сразу после этого вы обновляете некоторый элемент пользовательского интерфейса. Теперь, что было бы более естественным для вас? Вы бы предпочли неявно вернуться в ту же "среду", в которой вы были раньше (поток пользовательского интерфейса), или вы бы предпочли, чтобы вам пришлось явно указать, что вы хотите вернуться в ту же среду?

Я думаю, что первое "чувствует" более естественным для пользователя.

Пример:

public Task FetchAndReturnAsync(string url)
{
    var httpClient = new HttpClient();
    return httpClient.GetAsync(url);
}

Вы называете это так:

var awesomeUiResult = await FetchAndReturnAsync("http://www.google.com");
textBox.Text = awesomeUiResult;

Как вы думаете, что должно произойти? Является ли более естественным для вас возможность обновлять текстовое поле после ожидания или оно перестало работать, потому что вы сейчас находитесь вне исходного контекста?

ASP.NET требует использования того же SynchronizationContext для асинхронных операций в контроллере, в противном случае он блокирует работающий поток.

Вроде, но не совсем. ASP.NET создает SynchronizationContext для каждого входящего запроса, и этот контекст вообще не привязан к конкретному потоку. Тем не менее, только один поток одновременно может войти в контекст, поэтому, если второй поток попытается войти в него, пока он уже находится в нем, он заблокирует этот поток.

с какой целью Microsoft использовала тот же SynchronizationContext?

SynchronizationContext для ASP.NET управляет данными по запросу, такими как HttpContext.Current, культура и личность пользователя.

действительно ли хорошая практика использовать ConfigureAwait(false) везде, где это возможно?

Как правило, да, особенно для универсальных библиотек. На ASP.NET ConfigureAwait(false) мало что дает - в некоторых случаях незначительное увеличение производительности. Его можно использовать, чтобы избежать тупика, о котором я упоминал в своей статье, но его гораздо лучше (особенно в ASP.NET) использовать async весь путь.

Другие вопросы по тегам