Какова лучшая практика для асинхронного программирования в ASP.NET MVC?
Согласно этой статье ASP.NET требует использования того же SynchronizationContext
для асинхронных операций в контроллере, иначе он блокирует работающий поток. В заключение автор упомянул, что мы должны использовать оба метода как лучший метод для предотвращения взаимоблокировок потоков:
- В вашей "библиотеке" асинхронных методов используйте ConfigureAwait(false), где это возможно.
Не блокируйте задачи; использовать асинхронный режим до конца.
Примечание. Лучше всего применять обе передовые практики. Любой из них предотвратит тупик, но оба должны применяться для достижения максимальной производительности и скорости реагирования.
Но с какой целью 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
весь путь.