Розмивання ліній між асинхронією та звичайними функціями в C # 5.0


10

Останнім часом я не можу отримати достатню кількість дивовижної картини асинхронного очікування C # 5.0. Де ти був все моє життя?

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

Люди називають це зараженням зомбі . Коли асинхрон покусає ваш код, він буде збільшуватися і збільшуватися. Процес перенесення не складний, це просто asyncвведення в декларацію та завершення повернення значення Task<>. Але прикро це робити знову і знову під час перенесення старого синхронного коду.

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

Я думаю, що це може спрацювати, якщо ми будемо дотримуватися цих правил:

  1. Функції асинхронізації більше не потребуватимуть asyncдекларації. Їхні типи повернення не потрібно було б зафіксувати Task<>. Компілятор під час компіляції визначить функцію асинхронізації та автоматично виконає обгортання Завдання <>.

  2. Більше немає дзвінків із зашкодою та забуттям функцій асинхронізації. Якщо ви хочете зателефонувати у функцію асинхронізації, її потрібно буде почекати. Я навряд чи використовую вогнезахисні речі, і всі приклади шалених перегонів або тупиків завжди, здається, базуються на них. Я вважаю, що вони занадто заплутані і "не в контакті" з синхронним мисленням, яке ми намагаємося використовувати.

  3. Якщо ви дійсно не можете жити без пожежі та забуття, для цього буде спеціальний синтаксис. У будь-якому випадку це не буде частиною простого уніфікованого синтаксису, про який я говорю.

  4. Єдине ключове слово, яке вам потрібно для позначення асинхронного дзвінка, - це await. Якщо ви чекали, дзвінок асинхронний. Якщо ви цього не зробите, виклик є звичайним старим синхронним (пам’ятайте, у нас більше немає вогню та забуття).

  5. Компілятор автоматично визначить функції асинхронізації (оскільки вони вже не мають спеціальної декларації). Правило 4 робить це дуже просто - якщо функція має awaitвиклик всередині, це асинхронізація.

Чи може це працювати? чи я щось пропускаю? Цей уніфікований синтаксис набагато більш текучий і може взагалі вирішити зараження зомбі.

Деякі приклади:

// assume this is an async function (has await calls inside)
int CalcRemoteBalanceAsync() { ... }

// assume this is a regular sync function (has no await calls inside)
int CalcRemoteBalance() { ... }

// now let's try all combinations and see what they do:

// this is the common synchronous case - it will block
int b1 = CalcRemoteBalance();

// this is the common asynchronous case - it will not block
int b2 = await CalcRemoteBalanceAsync();

// choosing to call an asynchronous function in a synchronous manner - it will block
// this syntax was used previously for async fire-and-forget, but now it's just synchronous
int b3 = CalcRemoteBalanceAsync();

// strange combination - it will block since it's calling a synchronous function
// it should probably show a compilation warning though
int b4 = await CalcRemoteBalance();

Примітка: це продовження цікавої пов’язаної дискусії в СО


3
Ви завжди чекаєте на свої асинхронні операції? Скажіть, будь ласка, ви не робите цього одразу після того, як стріляєте з них ...
Джиммі Хоффа

1
Крім того, одна з чудових речей про асинхронізацію полягає в тому, що вам не доведеться awaitнегайно. Можна зробити щось на кшталт var task = FooAsync(); Bar(); await task;. Як би я це зробив у вашій пропозиції?
svick

3
Так ведете дискусію? Де мій BFG-3000 ...
Роберт Харві

2
@talkol Ви вважаєте, що паралельне програмування нецензурне? Це найменше цікавий світогляд, коли ви говорите async. Я думаю, що це одна з головних переваг async- await: те, що дозволяє легко складати асинхронні операції (і не тільки найпростішим способом "запустити A, чекати A, запустити B, чекати B"). І вже для цього є спеціальний синтаксис: він називається await.
svick

1
@svick ха-ха, зараз ми пішли туди :) Я не думаю, що паралельна прога нецензурна, але я думаю, що це робити з async-wait. Async-await - це синтаксичний цукор для збереження вашого синхронного душевного стану, не платячи ціну блокування. Якщо ви вже паралельно думаєте, я б закликав вас використовувати інший зразок
talkol

Відповіді:


9

На ваше запитання вже відповідає відповідь на питання, яке ви пов’язали.

Мета асинхронізації / очікування - полегшити запис коду у світі з багатьма операціями з високою затримкою. Переважна більшість ваших операцій не мають високої затримки.

Коли WinRT вперше вийшов, дизайнери описували, як вони вирішили, які операції будуть асинхронні. Вони вирішили, що все, що збирається зайняти 50 мс і більше, буде асинхронністю, а решта методів - звичайними, несинхронними методами.

Скільки методів довелося переписати, щоб зробити їх асинхронними? Близько 10 відсотків з них. Інші 90% взагалі не постраждали.

Ерік Ліпперт продовжує пояснювати досить вагомі технічні деталі, чому вони вирішили не застосовувати підхід одного розміру. Він в основному говорить , що asyncі awaitце часткова реалізація продовження обходячи стиль, і що оптимізація такого стилю , щоб відповідати всім випадкам є важким завданням.


Зверніть увагу на істотну різницю між питанням про ТА та цим питанням. SO запитує, чому б не зробити все асинхронізуючим. Тут ми цього не пропонуємо, ми пропонуємо зробити 10% асинхронізацію, просто використовуючи той самий синтаксис, що це все. Використання ближчого синтаксису має перевагу в тому, що ви можете легше змінити 10% асинхронізації, не зазнаючи ефектів доміно від цих змін
talkol

Мені трохи незрозуміло, чому asyncможна створити зомбі-інвазії. Навіть якщо метод викликає 10 інших методів, вам не доведеться просто asyncвищий рівень?
Роберт Харві

6
Скажімо, 100% мого поточного коду - це синхронізація. Тепер у мене є одна внутрішня функція рівня листів, яка запитує БД, яку я хотів би змінити на асинхронізацію. Тепер, щоб зробити його асинхронним, мені потрібно, щоб його абонент був асинхронним, а його викликав - асинхронним, і так далі до верхнього рівня. Звичайно, я говорю про випадок, коли очікується весь ланцюг (щоб зберегти синхронну конструкцію коду або пропустити зворотні долини)
talkol
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.