Ефективне змішування методів синхронізації та асинхронізації в рамках одного методу?


11

Гаразд, це звучить дивно, але код дуже простий і добре пояснює ситуацію.

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більше. Шаблон складається з наступного ланцюга подій:

  1. Чекайте початкового дзвінка, який отримує мені інформацію, над якою мені потрібно працювати
  2. Працюйте над цією інформацією синхронно
  3. Чекайте остаточного дзвінка, який зберігає оновлену роботу

Наведений вище код коду, як правило, я працюю над цими методами. Я awaitперший дзвінок, який мені належить, тому що він асинхронний. Далі я виконую ту роботу, яку мені потрібно робити, яка не пов'язана з IO чи ресурсами, і тому не є асинхронізованою. Нарешті, я зберігаю свою роботу, яка також є asyncдзвінком, і поза вантажним культом я awaitїї.

Але це найбільш ефективний / правильний спосіб поводження з цією схемою? Мені здається, я міг пропустити awaitостанній дзвінок, але що робити, якщо він не працює? І чи варто використовувати такий Taskметод, як ContinueWithланцюг моєї синхронної роботи з оригінальним викликом? Я просто зараз, коли я не впевнений, чи правильно впораюся з цим.

Враховуючи код у прикладі , чи є кращий спосіб обробляти цей ланцюжок викликів методу async / sync / async?


Код мені здається максимально коротким і зрозумілим. Все, що я можу придумати, вводить зайву складність.
Ейфорія

@Euphoric: Чи варто мені турбуватися, чекаючи останнього дзвінка? Що буде, якщо я цього не зробив і кинув? Чи буде це інакше, як зараз?
Зірвано

Відповіді:


3

Так, я думаю, що це правильний спосіб зробити це.

Не можна пропустити другу await. Якби це зробити, метод, здавалося б, завершився занадто рано (до того, як видалення було фактично виконано), і ви ніколи не дізнаєтесь, чи не вдалося видалити.

Я не бачу, як би тут ContinueWith()чи щось подібне допомогло. Ви можете використовувати його, щоб уникнути використання await, але це зробить ваш код складнішим і менш читабельним. І в цьому вся суть await: спрощення написання асинхронного коду у порівнянні з використанням продовження.


0

Спосіб управління цією схемою полягає в тому, щоб забезпечити асинхронність усіх вводу-виводу. Синхронні методи вводу / виводу призводять до блокування поточного потоку, поки він чекає відповіді з пункту призначення вводу / виводу (мережа, файлова система тощо).

Ще одна річ, яку слід врахувати, це те, що awaitслід використовувати, коли вам потрібно повернути значення або коли вам потрібен очікуваний код, щоб закінчити, перш ніж робити щось інше. Якщо жодна з цих речей вам не потрібна, ви можете "запустити і забути" ваш метод асинхронізації Task.Run.

Таким чином, для найефективнішого використання обчислювальних ресурсів, якщо RemoveRolesякийсь I / O, він повинен стати, 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 -код


2
Ніколи не слід стріляти і забувати код за допомогою Task.Run. На всі завдання слід чекати. Якщо ви не чекаєте Завдання, а завдання збирається сміттям у стані, коли виняток є "незарезервованим", це призведе до того, що UnobservedTaskException буде піднято під час виконання та може вивести з ладу вашу програму залежно від версії рамки.
Трайко
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.