async & wait - опитування на альтернативи [закрито]


15

Тепер, коли ми знаємо, що є у продажу для №5, нам, мабуть, ще є відкриття для впливу на вибір двох нових ключових слів для « Асинхронії », які вчора оголосив Андерс Хейсберг на PDC10 .

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

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

Будь ласка - одна пропозиція на відповідь, дублікати будуть нульовими.


До речі, "асинхронне програмування з інтегрованою мовою" дає нам LIAP, не зовсім згортаючи мову так само, як LINQ;)
Benjol

1
Якщо тільки не виражений її стрибок.
Конрад Фрікс

3
Але "Асинхронний режим роботи з інтегрованою мовою" добре звучить.
гленатрон

Це повинно бути поза темою.
DeadMG

Відповіді:


6

Зважаючи на те, що мені не зрозуміло сенс / необхідність async, я не можу з цим дійсно сперечатися, але найкраща моя пропозиція щодо заміни await:

yield while (дивіться! нових ключових слів немає)

Зауваживши , подумавши про це трохи більше, мені цікаво, чи повторне використання whileцього способу є хорошою ідеєю - природною тенденцією було б сподіватися на булеве згодом.

(Думає: знайти хороші ключові слова - це як знайти хороші доменні імена :)


+1, і, до речі, ви побили мене, щоб коментувати його запис у блозі за 7 хвилин ...
Зауважте, що я самостійно - придумайте ім'я

Але вам не обов’язково поступатися виконанню, якщо завдання вже виконано. Але ви завжди чекаєте завершення завдання (хоча і ніколи не чекаєте).
Аллон Гуралнек

@Allon Не обов'язково запускати тіло циклу будь- while(x) {...}якого, якщо xце значення false.
Зауважте, що самостійно - придумайте ім’я

@ Примітка: Ну, немає дієслова в while. Якщо ви додасте дієслово, наприклад do, ви отримуєте do {...} while (x), що виконує тіло незалежно від x (принаймні один раз). Ваша пропозиція yield whileздається дуже схожою на do while, але з протилежними гарантіями виконання дієслова, що може бути дещо оманливим (але не дуже великим). Мені найбільше не подобається те yield, що вона передбачає реалізацію механізму. Вся справа в async/ awaitполягає в тому, що ви пишете асинхронну операцію в синхронному стилі. yieldпорушує цей синхронний стиль.
Аллон Гуралнек

Чи нове ключове слово обов'язково погано? Як я розумію, awaitключове слово буде розпізнане за контекстом, тож ви все ще можете мати метод або змінну з назвою "очікувати", якщо хочете. Я певною мірою вважаю, що використання нового ключового слова для нової функціональності є менш заплутаним, ніж повторне використання існуючого ключового слова, щоб означати більше ніж одне. (перебільшений приклад: nevarmouse.net/esoteric/ook.html )
Тім Гудман

5

Як щодо того, щоб не було ключового слова?

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

Document doc = DownloadDocumentAsync();

Це воно. Причина, чому люди важко придумують ключове слово для цієї речі, це тому, що це як ключове слово для "робити те, що ти робив би, якби справи були абсолютно нормальними". Це повинно бути за замовчуванням, не вимагати ключового слова.

Оновлення

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

Ми винаходимо атрибут: [AutoAwait]. Це можна застосувати лише до методів. Один із способів застосувати це до вашого методу - позначити його async. Але ви також можете це зробити вручну, наприклад:

[AutoAwait]
public Task<Document> DownloadDocumentAsync()

Тоді всередині будь-якого asyncметоду компілятор припустить, що ви хочете чекати на виклик DownloadDocumentAsync, тому не потрібно його вказувати. Будь-який дзвінок до цього методу автоматично його чекає.

Document doc = DownloadDocumentAsync();

Тепер, якщо ви хочете "стати розумним" і отримати його Task<Document>, ви використовуєте оператор start, який може з'явитися лише перед викликом методу:

Task<Document> task = start DownloadDocumentAsync();

Охайний, я думаю. Тепер звичайний виклик методу означає те, що він зазвичай означає: чекайте завершення методу. І startвказує на щось інше: не чекай.

Для коду, який з’являється поза asyncметодом, єдиний спосіб, яким ви можете викликати [AutoAwait]метод, - це префіксація start. Це змушує вас писати код, який має однакове значення незалежно від того, відображається він у asyncметоді чи ні.

Тоді я починаю жадібний! :)

По-перше, я хочу asyncзастосувати методи інтерфейсу:

interface IThing
{
    async int GetCount();
} 

Це в основному означає, що метод реалізації повинен повернутися Task<int>або щось сумісне з await, і абоненти до методу отримають [AutoAwait]поведінку.

Також коли я реалізую вищевказаний метод, я хочу мати можливість писати:

async int GetCount()

Тому я не повинен згадувати Task<int>як тип повернення.

Також я хочу asyncзвернутися до типів делегатів (які, зрештою, схожі на інтерфейси одного методу). Так:

public async delegate TResult AsyncFunc<TResult>();

asyncДелегат має - ви вже здогадалися - [AutoAwait]поведінка. З asyncметоду ви можете викликати його, і він буде автоматично awaitвідредагований (якщо ви не вирішите його просто start). Так що, якщо ви кажете:

AsyncFunc<Document> getDoc = DownloadDocumentAsync;

Це просто працює. Це не виклик методу. Жодне завдання ще не розпочато - async delegateце не завдання. Це фабрика для виконання завдань. Ви можете сказати:

Document doc = getDoc();

І це почне завдання і чекатиме його закінчення і дасть результат. Або ви можете сказати:

Task<Document> t = start getDoc();

Тож одне місце в цьому місці, де "сантехніка" протікає, це те, що якщо ви хочете зробити делегат asyncметоду, ви повинні знати async delegateтип. Тож замість Funcвас треба сказати AsyncFuncтощо. Хоча одного дня такі речі можуть бути виправлені за допомогою поліпшеного виводу типу.

Інше питання - що має статися, якщо ви скажете почати із звичайного (несинхронного) методу. Очевидно, що помилка компіляції була б безпечним варіантом. Але є й інші можливості.


Це може бути здійснено при неявному перетворенні, але в іншому випадку вимагається оцінка праворуч зліва направо (що прямо протилежне тому, як працює компілятор, за винятком лямбда). Я думаю, що я все-таки буду проти цього, оскільки це перешкоджає використанню var, можливо, замінюючи якесь довге явне ім'я типу, а також є неоднозначним між awaitвипадком і випадком, коли хтось випадково викликав метод асинхронізації замість звичайного синхронного методу. Спочатку здається інтуїтивно зрозумілим, але насправді порушує принцип найменшого здивування.
Aaronaught

@Aaronaught - Чому це перешкоджає використанню var? Цікаво, чи відповідаєте ви на попередню редакцію моєї відповіді ... Я повністю її переписав. Тепер ви можете подумати про цю пропозицію так: якщо метод позначений спеціальним атрибутом, це як би awaitключове слово автоматично вставляється перед викликами цього методу (якщо ви не придушите це за допомогою startпрефікса). Все залишається точно так само, як у CTP, а значить, varпрацює чудово.
Даніель Ервікер

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

1
Мені подобається ваша інверсія ключового слова, що очікує. І мені також не подобається потрійне надмірність public async Task<int> FooAsync().
Аллон Гуралнек

1
Так, я бачу, що умова іменування Async-postfix є ознакою того, що щось може бути зафіксовано більш формально. В основному, якщо є правило, яке говорить "такі методи повинні бути названі певним чином, щоб люди знали, як їх правильно називати", то звідси випливає, що те саме правило можна було б використовувати для атрибуції цих методів певним чином, а потім компілятор допоможе вам правильно їх зателефонувати.
Даніель Ервікер

4
hearken unto AsyncFetch(…)

(якщо ви цього не отримаєте, прочитайте запис у блозі Еріка . Принаймні, це краще, ніж for sooth Romeo wherefore art thou AsyncFetch(…))


(тих, хто не має почуття гумору, не потрібно застосовувати)
Зауважте, що потрібно самостійно придумати ім’я

4

Я думаю, що asyncце добре, але, можливо, це тому, що я асоціюю його зі сторінками асинхронізації ASP.NET - та сама ідея.

Для awaitключового слова я віддаю перевагу continue afterабо resume after.

Мені не подобається yieldчи будь-який його варіант, тому що семантика така, що метод ніколи насправді не може виконати; це залежить від стану завдання.


Мені подобається resume afterв await. Можливо, asyncможна було б назвати resumable.
Тім Гудман

Хоча я віддаю перевагу просто after, continue afterпідхід має сильну перевагу в реалізації: він включає в себе вже існуюче контекстне ключове слово, але з синтаксисом, який не сумісний із поточним використанням. Це гарантує, що додаток ніколи не порушить існуючий код. Використовуючи абсолютно нове ключове слово, реалізація повинна впоратися з можливим використанням цього слова як ідентифікатора для більш старого коду, що може отримати досить складно.
Edurne Pascual

3

Я також додав коментарі до блогу Еріка, я не бачу проблем із використанням одного і того ж ключового слова async

var data = async DownloadFileAsync(url);

Я просто висловлюю, що хочу завантажити файл асинхронно. Тут є трохи надмірності, "async" з'являється двічі, тому що це також у назві методу. Компілятор міг би бути надзвичайно розумним і виявити умовність, що методи, що закінчуються на "Асинхроні", є методами асинхронності без фактів, і додамо це для нас у компільований код. Тож замість цього ви можете просто зателефонувати

var data = async DownloadFile(url);

на відміну від виклику синхронного

var data = DownloadFile(url);

Хек, ми також повинні мати можливість визначати їх так само, оскільки ключове слово async є в нашій декларації, чому ми повинні вручну додавати "Async" до кожного імені методу - компілятор може це зробити для нас.


Мені подобається цукор, який ти додав, хоча хлопці на C #, мабуть, не пітимуть на це. (FWIW, вони вже роблять щось подібне при пошуку імен атрибутів)
примітка до себе - придумати назву

2
А враховуючи, що asyncключове слово в методі - це лише вибагливість (якщо я правильно зрозумів), мені цікаво, чи не найкраще було б робити протилежне тому, що ви пропонуєте: відмовитися asyncвід методу та просто використовувати його де вони зараз є await.
Benjol

Це можливість. Ключове слово асинхрон у методі декарації дійсно існує для чистоти. Я б хотів, щоб він зберігався там, але без вимоги до нас додавати "Async" до імен методів. Наприклад, async Task<Byte[]> DownloadFile(...)а не Task<Byte[]> DownloadFileAsync(...)(Останнє все одно буде складеним підписом). Будь-який спосіб працює.
Марк Н

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

3

async = task - це модифікація функції для повернення завдання, то чому б не використати ключове слово "task"?

очікувати = закінчити - нам не обов’язково чекати, але завдання потрібно «закінчити» перед тим, як використовувати результат.


Тут справді важко сперечатися з простотою.
sblom

2

Мені подобається yield until. yield while, вже запропоновано, це чудово і не вводить нових ключових слів, але я думаю, що "до" захоплює поведінку трохи краще.

Я думаю yield <something>, що це відмінна ідея, оскільки вихід вже захоплює ідею зробити так, щоб решта методу була такою вдалою. Можливо, хтось може придумати краще слово, ніж «поки».


2

Я просто хочу зареєструвати свій голос за пропозицію Аарона G comefrom- перше відповідне використання, яке я бачив у заяві ІНТЕРКАЛ COMEFROM . Ідея полягає в тому, що це щось протилежне GOTO (відскакує від заяви GOTO), оскільки воно робить деяке місце у вашому коді перейти до оператора COMEFROM.


2

Оскільки ми маємо справу з Task<T>s, як щодо використання startключового слова, що передує оператору, як у:

start var document = FetchAsync(urls[i]);


Гммм, можливо, finishбуло б навіть краще, ніж start?
головний герой

1

Варто відзначити, що F # також використовує asyncключове слово у своїх робочих процесах асинхронізації, що майже все те саме, що і новий функціонал async у C # 5. Тому я б утримував це однаково

Для awaitключового слова у F # вони просто використовують let!замість let. У C # немає однакового синтаксису призначення, тому їм потрібно щось праворуч від =знака. Як сказав Бенжол, він функціонує так само, як і yieldтак, майже це повинен бути варіант.


1
"Очікувати" взагалі не повинно бути у дорученні (хоча, звичайно, це так і є). Це законно як оператор майже будь-якого виразу, який має тип, на якому ми можемо знайти GetAwaiter. (Точні правила ще опрацьовуються до оприлюднювальної форми.)
Ерік Ліпперт

1
@Eric, у F # це було б do!, але ти це знав ...
Benjol

1

yield async FetchAsync(..)


Це ідеально відповідає asyncмодифікатору, який потрібно застосувати до методу, до якого ви посилаєтесь. А також семантичністю поточного yield return, тобто ви повертаєтесь і даєте виконання виконуваному коду, що перераховує, в цьому випадку ви віддаєте своє виконання асинхронному методу.

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

Чесно кажучи, я не зовсім розумію аргумент "не приносять виконання". Зрештою, хіба не сенс викликати інший метод - це «принести виконання» цьому методу? Незалежно від того, асинхронний він чи ні? Я щось тут пропускаю?

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

ІМО. Я думаю, що різні "незадовільні" випадки є деталізацією реалізації. Я б краще поручився за послідовність мови (тобто повторне використання yield).


0

Як щодо того complete, як у "Я хочу, щоб завдання було виконане"?

Task<byte[]> downloadTask = DownloadFileAsync(url);
byte[] data = complete downloadTask;

1
Чому потік? Ввічливість - принаймні, пояснити себе після відмови.
Аллон Гуралнек

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.