#c# #async-await #blazor
#c# #асинхронное ожидание #blazor
Вопрос:
Я разрабатываю серверное веб-приложение Blazor, и я разрываюсь при использовании .ConfigureAwait(false)
. Я знаю, что мне не следует использовать его в коде компонента, но я не уверен насчет вложенного кода. Например: я вызываю асинхронный метод для обновления данных на странице при нажатии кнопки. Сам этот метод также вызывает метод async для выполнения запроса к API. Вопрос в том, следует ли вызывать этот вложенный метод (запрос API) с ConfigureAwait(false)
помощью или нет?
Редактировать: добавление кода для более наглядной демонстрации:
Этот метод вызывается с кнопки OnClick
protected async Task GridRefresh()
{
Models = null;
_errorMessage = null;
try
{
Models = await _dataService.GetDataAsync(Active);
}
catch (Exception ex)
{
_errorMessage = ex.Message.Split("Message:").Last();
}
}
Этот метод находится в DataService
классе, который я добавляю в свой компонент с помощью внедрения зависимостей
public async Task<IEnumerable<ItemViewModel>> GetDataAsync(int id)
{
var container = await ApiWrapper.getByIDAsync(id);
if(container == null)
throw new Exception($"Container with code {id} not found!");
return container.Items;
}
Вопрос: должен ли я использовать ConfigureAwait
GetDataAsync
метод in?
var container = await ApiWrapper.getByIDAsync(id).ConfigureAwait(false);
Комментарии:
1. Это зависит от того, что вы делаете после
await
. Можете ли вы показать пример своего кода?2. Что вы имеете в виду
nested
? Руководство предназначено не для компонентов , а для библиотек или «другого» кода в целом, чтобы позволить коду верхнего уровня решать, возвращаться ли к исходному коду синхронизации, например, к потоку пользовательского интерфейса. Библиотеки должны использоватьConfigureAwait(false)
. Это касается вашего собственного кода, отличного от пользовательского интерфейса. Код пользовательского интерфейса не должен , потому что для обновления пользовательского интерфейса ему необходимо вернуться к исходному контексту3. Пожалуйста, покажите свой код……
4. @TheodorZoulias Я добавил код для редактирования
Ответ №1:
Вы должны сделать то же самое, что и в настольных приложениях.
- Чтобы обновить пользовательский интерфейс, вы должны быть в его контексте синхронизации, поэтому вам не следует использовать
ConfigureAwait(false)
при обновлении пользовательского интерфейса. - В коде, отличном от пользовательского интерфейса, нет необходимости возвращаться к контексту пользовательского интерфейса, поэтому вам, вероятно, следует использовать
ConfigureAwait(false)
и позволить коду верхнего уровня решить, нужно ли ему возвращаться к контексту пользовательского интерфейса. Таким образом, вы избегаете возможных взаимоблокировок, если поток / контекст пользовательского интерфейса занят. - В библиотечном коде вы должны использовать
ConfigureAwait(false)
, чтобы избежать принудительного переключения контекста и, возможно, взаимоблокировки
В небольших чистых компонентах (таких компонентах, которые вы должны создавать в любом случае) не должно быть такой большой проблемы, если вы опустите ConfigureAwait(false)
.
В библиотечном коде пропуск ConfigureAwait(false)
может легко привести к взаимоблокировкам, особенно во время отладки. На самом деле, мне интересно, вызваны ли тупики, с которыми сталкивается какой-либо популярный локальный пакет хранения, некоторыми пропущенными ConfigureAwait
вызовами
Ответ №2:
Давайте рассмотрим GetDataAsync
метод:
public async Task<IEnumerable<ItemViewModel>> GetDataAsync(int id)
{
var container = await ApiWrapper.getByIDAsync(id);
if(container == null)
throw new Exception($"Container with code {id} not found!");
return container.Items;
}
После ожидания getByIDAsync
будут выполнены две строки кода:
if(container == null)
throw new Exception($"Container with code {id} not found!");
return container.Items;
В настоящее время эти две строки будут выполняться в контексте синхронизации Блейзора. Требуется ли это? Это зависит от кода, выполняемого в методе доступа к Items
свойству container
объекта. Есть ли какой-либо код, который взаимодействует с каким-либо компонентом пользовательского интерфейса? Если да, или если вы не уверены, то вам не следует добавлять ConfigureAwait(false)
ожидание getByIDAsync
. Если нет (что наиболее вероятно), то использование ConfigureAwait(false)
не будет иметь негативных последствий. Положительные аспекты добавления ConfigureAwait(false)
— это то, что позволит вызывающей стороне блокировать асинхронный код без взаимоблокировки вашего приложения:
Models = _dataService.GetDataAsync(Active).Resu< // Bad practice
Добавление ConfigureAwait(false)
также сделает ваш код немного более эффективным. И под небольшим я подразумеваю практически незначительный. Так что основывайте свое решение не на этом, а на том, что делает код, и на том, как он предназначен для использования.
Ответ №3:
Те же правила применяются к любой структуре пользовательского интерфейса.
ConfigureAwait(false)
, обеспечивает очень небольшую эффективность. Однако, как правило, вы бы не включали его в код пользовательского интерфейса верхнего уровня.
Чтобы ответить на вопрос напрямую:
Этот метод находится в классе DataService, который я добавляю в свой компонент с помощью внедрения зависимостей. должен ли я использовать ConfigureAwait в методе GetDataAsync?
В этом сценарии все в порядке.
Долгая история
Blazor использует контекст синхронизации (SynchronizationContext) для принудительного выполнения одного логического потока выполнения. Методы жизненного цикла компонента и любые обратные вызовы событий, вызываемые Blazor, выполняются в контексте синхронизации.
Контекст синхронизации сервера Blazor пытается эмулировать однопоточную среду, чтобы она точно соответствовала модели WebAssembly в браузере, которая является однопоточной. В любой данный момент времени работа выполняется ровно в одном потоке, создавая впечатление единого логического потока. Никакие две операции не выполняются одновременно.
Короче говоря, он выполняется в одном потоке пользовательского интерфейса и со своим собственным контекстом синхронизации, поэтому не вызывайте ConfigureAwait(false)
код верхнего уровня, просто используйте ту же логику, что и в любой другой среде пользовательского интерфейса, которая имеет контекст синхронизации для перекачки сообщений или диспетчера.
Который, по-видимому, подкреплен SteveSandersonMS в этом вопросе github Blazor Вопрос: ConfigureAwait(false) в Blazor на стороне сервера?
Да, Blazor Server — это платформа пользовательского интерфейса, которая требует, чтобы ваш код выполнялся в контексте синхронизации. Поэтому не используйте ConfigureAwait(false) в коде вашего компонента Blazor.
а также из mkArtakMSFT в этом вопросе github Руководство по использованию ConfigureAwait(…) в асинхронных методах жизненного цикла в компоненте Razor
Наше руководство состоит в том, чтобы просто не использовать ConfigureAwait в Blazor.
…
Только для сценариев, связанных с пользовательским интерфейсом. Полезно для сценариев, не влияющих на пользовательский интерфейс (запрос хранилища данных или службы)
ConfigureAwait(false)
.
Комментарии:
1. Вопрос касается другого кода — кода, который не обновляет пользовательский интерфейс напрямую
2. @TheGeneral Я добавил код для редактирования, чтобы более точно понять, о чем я спрашиваю