Хорошо, это звучит странно, но код очень прост и хорошо объясняет ситуацию.
public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
AssertNotDisposed();
var roles = await GetRolesForUser(user);
roles.Roles = RemoveRoles(roles.Roles, role);
await Run(TableOperation.Replace(roles));
}
(Я знаю, что говорю вроде в абстрактном ниже, но выше это фактический метод из того, что будет на самом деле производственный код, который на самом деле делает то, о чем я здесь спрашиваю, и я на самом деле заинтересован в том, чтобы вы на самом деле рецензирование это для корректности по отношению к шаблону асинхронности / ожидания.)
Я сталкиваюсь с этим паттерном все чаще и чаще сейчас, когда использую async
/ await
больше. Шаблон состоит из следующей цепочки событий:
- Жду первоначального звонка, который дает мне некоторую информацию, с которой мне нужно поработать
- Работать с этой информацией синхронно
- Жду окончательного звонка, который сохраняет обновленную работу
Приведенный выше блок кода, как правило, относится к обработке этих методов. Я await
первый звонок, который я должен, потому что это асинхронный. Затем я выполняю работу, которая мне необходима, которая не связана с вводом-выводом или ресурсами, и поэтому не асинхронна. Наконец, я сохраняю свою работу, которая также является async
вызовом, и из-за груза культа я await
его.
Но является ли это наиболее эффективным / правильным способом обработки этого паттерна? Мне кажется, я мог бы пропустить await
последний звонок, но что, если он потерпит неудачу? И должен ли я использовать такой Task
метод, ContinueWith
чтобы связать мою синхронную работу с исходным вызовом? Я сейчас нахожусь в точке, где я не уверен, правильно ли я это делаю.
Учитывая код в примере , есть ли лучший способ обработать эту цепочку вызовов метода async / sync / async?
источник
Ответы:
Да, я думаю, что это правильный способ сделать это.
Вы не можете пропустить второй
await
. Если бы вы это сделали, метод, казалось бы, завершился слишком рано (до того, как на самом деле было выполнено удаление), и вы никогда бы не узнали, не удалось ли удалить.Я не вижу, как
ContinueWith()
или что-то подобное поможет здесь. Вы можете использовать его, чтобы избежать использованияawait
, но это сделает ваш код более сложным и менее читабельным. И в этом весь смыслawait
: упростить написание асинхронного кода по сравнению с использованием продолжений.источник
Способ обработки этого шаблона состоит в том, чтобы гарантировать, что весь ввод / вывод является асинхронным. Методы синхронного ввода / вывода заставляют текущий поток блокироваться, пока он ожидает ответа от пункта назначения ввода / вывода (сеть, файловая система и т. Д.).
Еще одна вещь, которую следует учитывать, это то, что
await
следует использовать, когда вам нужно возвращаемое значение или когда вам нужно завершить ожидаемый код, прежде чем делать что-то еще. Если вам не нужна ни одна из этих вещей, вы можете «запустить и забыть» свой асинхронный метод с помощьюTask.Run
.Таким образом, для наиболее эффективного использования вычислительных ресурсов, если
RemoveRoles
выполняется какой-либо ввод-вывод, он должен стать,await RemoveRolesAsync
и методы ввода-вывода, вызываемые посредством,RemoveRolesAsync
также должны быть асинхронными (и, возможно, ожидаемыми).Если производительность не является вашей главной заботой, тогда можно выполнить синхронный ввод-вывод в асинхронном потоке. Это технический долг, хотя. (В этом случае вам может потребоваться вызвать первый асинхронный метод в
ConfigureAwait
зависимости от того, где выполняется код.)Вот более детальный взгляд на лучшие практики - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Вот некоторые заметки о поведении ConfigureAwait в различных средах, таких как ASP.NET, WebAPI и т. Д. - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -код
источник