async & waitit - sondaggio per alternative [chiuso]


15

Ora che sappiamo cosa è in serbo per c # 5, sembra che ci sia ancora un'apertura per influenzare la scelta delle due nuove parole chiave per " Asynchony " che sono state annunciate ieri da Anders Heijsberg al 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);
    }
}

Eric Lippert ha una spiegazione della scelta delle attuali due parole chiave e del modo in cui sono state fraintese negli studi di usabilità. I commenti hanno diverse altre proposte.

Per favore - un suggerimento per risposta, i duplicati verranno cancellati.


A proposito, la "programmazione asincrona integrata nella lingua" ci dà LIAP, non si discosta affatto dalla lingua allo stesso modo di LINQ;)
Benjol,

1
A meno che il suo balzo pronunciato.
Conrad Frix,

3
Ma gli acronimi "Runtime asincrono integrato nella lingua" aumentano bene.
glenatron,

Questo deve essere fuori tema.
DeadMG

Risposte:


6

Dato che non sono chiaro sul significato / necessità di async, non posso davvero discuterne, ma il mio miglior suggerimento per la sostituzione awaitè:

yield while (guarda! nessuna nuova parola chiave)

Nota che dopo averci pensato un po 'di più, mi chiedo se riutilizzarlo whilein questo modo sia una buona idea - la tendenza naturale sarebbe quella di aspettarsi un booleano in seguito.

(Pensa: trovare buone parole chiave è come trovare buoni nomi di dominio :)


+1 e, a proposito, mi hai battuto a commentare il suo post sul blog di 7 minuti ...
Nota per sé - pensa a un nome

Ma non stai necessariamente dando esecuzione se l'attività è già stata completata. Ma stai sempre aspettando il completamento dell'attività (anche se non stai mai aspettando).
Allon Guralnek,

@Allon Non esegui necessariamente il corpo del loop di while(x) {...}nessuno dei due, se xè falso.
Nota per se stessi: pensa a un nome il

@Nota: non c'è nessun verbo in while. Se aggiungi un verbo, come do, allora ottieni do {...} while (x), che esegue il corpo indipendentemente da x (almeno una volta). Il tuo suggerimento yield whilesembra molto simile do while, ma con garanzie opposte di eseguire il verbo, che potrebbe essere un po 'fuorviante (ma non molto di un grosso problema). La cosa che non mi piace di più yieldè che implica l'implementazione di un meccanismo. Il punto di async/ awaitè che si scrive un'operazione asincrona in uno stile sincrono. yieldrompe quello stile sincrono.
Allon Guralnek,

Una nuova parola chiave è necessariamente una cosa negativa? A quanto ho capito, la awaitparola chiave sarebbe riconosciuta dal contesto, quindi potresti avere ancora un metodo o una variabile chiamata "wait" se lo desideri. In una certa misura, penso che usare una nuova parola chiave per nuove funzionalità sia meno confuso che riutilizzare una parola chiave esistente per significare più di una cosa. (esempio esagerato: dangermouse.net/esoteric/ook.html )
Tim Goodman,

5

Che ne dici di non avere una parola chiave?

Vorrei che il compilatore si rendesse conto che, il più delle volte, quando chiamo un metodo asincrono, voglio il risultato.

Document doc = DownloadDocumentAsync();

Questo è tutto. Il motivo per cui le persone hanno difficoltà a pensare a una parola chiave per questa cosa è perché è come avere una parola chiave per "fai la cosa che faresti se le cose fossero perfettamente normali". Dovrebbe essere l'impostazione predefinita, non richiedere una parola chiave.

Aggiornare

Inizialmente ho suggerito che il compilatore dovrebbe diventare intelligente con l'inferenza del tipo per capire cosa fare. Ripensandoci, manterrei l'implementazione esistente nel CTP così com'è, ma farei un paio di banali aggiunte ad esso, in modo da ridurre i casi in cui dovresti usare awaitesplicitamente la parola chiave.

Inventiamo un attributo: [AutoAwait]. Questo può essere applicato solo ai metodi. Un modo per applicare questo al tuo metodo è contrassegnarlo async. Ma potresti anche farlo a mano, ad esempio:

[AutoAwait]
public Task<Document> DownloadDocumentAsync()

Quindi, all'interno di qualsiasi asyncmetodo, il compilatore supporrà di voler attendere una chiamata DownloadDocumentAsync, quindi non è necessario specificarlo. Qualsiasi chiamata a quel metodo lo attenderà automaticamente.

Document doc = DownloadDocumentAsync();

Ora, se vuoi "diventare intelligente" e ottenere il Task<Document>, usi un operatore start, che può apparire solo prima di una chiamata di metodo:

Task<Document> task = start DownloadDocumentAsync();

Pulito, penso. Ora una semplice chiamata di metodo significa ciò che di solito significa: attendere il completamento del metodo. E startindica qualcosa di diverso: non aspettare.

Per il codice che appare al di fuori di un asyncmetodo, l'unico modo in cui ti è consentito chiamare un [AutoAwait]metodo è prefissarlo con start. Questo ti costringe a scrivere codice che ha lo stesso significato indipendentemente dal fatto che appaia asynco meno in un metodo.

Quindi inizio a diventare avido! :)

In primo luogo, voglio asyncapplicare ai metodi di interfaccia:

interface IThing
{
    async int GetCount();
} 

Significa sostanzialmente che il metodo di implementazione deve tornare Task<int>o qualcosa di compatibile con await, e i chiamanti al metodo avranno [AutoAwait]comportamento.

Anche quando implemento il metodo sopra, voglio essere in grado di scrivere:

async int GetCount()

Quindi non devo menzionare Task<int>come tipo di ritorno.

Inoltre, voglio asyncfare domanda per i tipi delegati (che, dopo tutto, sono come interfacce con un metodo). Così:

public async delegate TResult AsyncFunc<TResult>();

Un asyncdelegato ha - hai indovinato - un [AutoAwait]comportamento. Da un asyncmetodo puoi chiamarlo e verrà automaticamente awaited (a meno che tu non scelga di startfarlo). E così se dici:

AsyncFunc<Document> getDoc = DownloadDocumentAsync;

Funziona e basta. Non è una chiamata di metodo. Nessuna attività è stata ancora avviata: async delegatenon è un'attività. È una fabbrica per svolgere compiti. Si può dire:

Document doc = getDoc();

E questo avvierà un'attività e aspetterà che finisca e ti dia il risultato. Oppure puoi dire:

Task<Document> t = start getDoc();

Quindi un posto in questo in cui fuoriesce "l'idraulico" è che se vuoi fare un delegato a un asyncmetodo, devi sapere di usare un async delegatetipo. Quindi, invece di Functe, devi dire AsyncFunc, e così via. Anche se un giorno quel genere di cose potrebbe essere risolto da una migliore inferenza di tipo.

Un'altra domanda è cosa dovrebbe accadere se dici di iniziare con un metodo ordinario (non asincrono). Ovviamente un errore di compilazione sarebbe l'opzione sicura. Ma ci sono altre possibilità.


Ciò potrebbe essere fattibile con una conversione implicita, ma altrimenti richiederebbe una valutazione da sinistra a destra dell'istruzione (che è esattamente l'opposto di come funziona normalmente il compilatore, ad eccezione di lambdas). Penso che sarei ancora contrario a questo perché impedisce l'uso var, potenzialmente di sostituire un nome di tipo esplicito lungo, ed è anche ambiguo tra il awaitcaso e il caso in cui qualcuno ha accidentalmente chiamato il metodo asincrono invece del normale metodo sincrono. All'inizio sembra intuitivo, ma in realtà viola il principio della minima sorpresa.
Aaronaught,

@Aaronaught - Perché ne impedisce l'uso var? Mi chiedo se stai rispondendo alla precedente revisione della mia risposta ... L'ho completamente riscritta. Ora puoi pensare a questo suggerimento come segue: se il metodo è contrassegnato da un attributo speciale, è come se la awaitparola chiave fosse automaticamente inserita prima delle chiamate a quel metodo (a meno che non lo sopprimessi con il startprefisso). Tutto rimane esattamente come nel CTP, e quindi varfunziona bene.
Daniel Earwicker,

In effetti ero ... strano che ho deciso di rivisitare questo thread e rispondere alla tua risposta quasi esattamente nello stesso momento in cui hai deciso di modificarlo. Dovrò rileggerlo ora ...
Aaronaught,

1
Mi piace la tua inversione della parola chiave wait. E non mi piace nemmeno la tripla ridondanza di public async Task<int> FooAsync().
Allon Guralnek,

1
Sì, vedo quella convenzione di denominazione Async-postfix come un segno che qualcosa potrebbe essere catturato in modo più formale. Fondamentalmente, se c'è una regola che dice "metodi come questo dovrebbero essere nominati in un certo modo, quindi le persone sanno come chiamarli correttamente", ne consegue che la stessa regola potrebbe essere usata per attribuire quei metodi in un certo modo, e quindi il compilatore aiutarti a chiamarli correttamente.
Daniel Earwicker,


4

Penso che vada asyncbene, ma forse è perché lo associo a pagine asincrone ASP.NET - stessa idea.

Per la awaitparola chiave che preferisco continue aftero resume after.

Non mi piacciono yieldo nessuna delle sue varianti, perché la semantica è tale che il metodo non può mai effettivamente dare esecuzione; dipende dallo stato dell'attività.


Mi piace resume afterper await. Forse asyncpotrebbe essere chiamato resumable.
Tim Goodman,

Anche se preferirei solo after, l' continue afterapproccio ha un forte vantaggio di implementazione: include una parola chiave contestuale attualmente esistente, ma con una sintassi non compatibile con l'uso corrente. Ciò garantisce che l'aggiunta non romperà mai il codice esistente. Quando si utilizza una parola chiave completamente nuova, l'implementazione deve far fronte ai possibili usi della parola come identificatore sul codice precedente, che può diventare piuttosto complicato.
Edurne Pascual,

3

Ho aggiunto anche commenti ai blog di Eric, non vedo alcun problema con l'utilizzo della stessa parola chiave async

var data = async DownloadFileAsync(url);

Sto solo esprimendo che voglio scaricare il file in modo asincrono. C'è un po 'di ridondanza qui, "asincrono" appare due volte, perché è anche nel nome del metodo. Il compilatore potrebbe essere molto più intelligente e rilevare la convenzione secondo cui i metodi che terminano in "Async" sono in effetti metodi asincroni e aggiungerlo per noi nel codice compilato. Quindi invece potresti semplicemente voler chiamare

var data = async DownloadFile(url);

al contrario di chiamare quello sincrono

var data = DownloadFile(url);

Diamine, dovremmo anche essere in grado di definirli allo stesso modo, poiché la parola chiave asincrona è presente nella nostra dichiarazione, perché dobbiamo aggiungere manualmente "Asincrono" al nome di ogni metodo - il compilatore può farlo per noi.


Mi piace lo zucchero che hai aggiunto, anche se probabilmente i ragazzi di C # non ci proveranno. (FWIW, fanno già qualcosa di simile quando cercano i nomi degli attributi)
Nota per se stessi - pensa a un nome

2
E dato che la asyncparola chiave sul metodo è solo una buona cosa (se ho capito bene), mi chiedo se la cosa migliore non sarebbe fare il contrario di ciò che suggerisci: abbandonare asyncil metodo e semplicemente usarlo dove hanno attualmente await.
Benjol,

Questa è una possibilità. La parola chiave asincrona sul metodo delcaration è proprio lì per pulizia davvero. Preferirei che rimanesse lì, ma senza l'obbligo per noi di aggiungere "Asincrono" ai nomi dei metodi. Ad esempio async Task<Byte[]> DownloadFile(...)anziché Task<Byte[]> DownloadFileAsync(...)(Quest'ultima sarà comunque la firma compilata). In entrambi i casi funziona.
Segna H

Onestamente non sono un fan neanche di questo. Come in un precedente commento, devo sottolineare che la tua versione finale viola il principio della minima sorpresa, poiché in realtà sta invocando un metodo completamente diverso da quello scritto, e l'implementazione e la firma di quel metodo sono interamente fino alla classe di implementazione . Anche la prima versione è problematica perché in realtà non dice nulla (eseguire in modo asincrono questo metodo asincrono?). Il pensiero che stiamo cercando di esprimere è una continuazione o un'esecuzione differita e questo non lo esprime affatto.
Aaronaught,

3

async = task - Sta modificando una funzione per restituire un'attività, quindi perché non usare la parola chiave "task"?

await = finish - Non dobbiamo necessariamente aspettare, ma l'attività deve "finire" prima di usare il risultato.


È davvero difficile discutere con la semplicità qui.
sblom,

2

Mi piace yield until. yield while, già suggerito, è eccezionale e non introduce nuove parole chiave, ma penso che "fino a" non capisca meglio il comportamento.

Penso che yield <something>sia una grande idea, perché la resa cattura già l'idea di rendere il resto del metodo una continuazione così bene. Forse qualcuno può pensare a una parola migliore di "fino a".


2

Voglio solo registrare il mio voto per il suggerimento di Aaron G comefrom- il primo uso appropriato che ho visto della dichiarazione COMEFROM di INTERCAL . L'idea è che è una specie di l'opposto di GOTO (saltando via dal l'istruzione GOTO) in quanto rende un posto nel codice di salto per la dichiarazione comefrom.


2

Dato che abbiamo a che fare con Task<T>s, che ne dici di usare startcome parola chiave prima dell'istruzione, come in:

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


Hmmm, forse finishsarebbe anche meglio di start?
Protagonista

1

Vale la pena notare che F # usa anche la asyncparola chiave nei suoi flussi di lavoro asincroni, che è praticamente la stessa cosa della nuova funzionalità asincrona in C # 5. Pertanto, lo terrei uguale

Per la awaitparola chiave in F #, usano semplicemente let!invece di let. C # non ha la stessa sintassi dell'assegnazione, quindi hanno bisogno di qualcosa sul lato destro del =segno. Come ha detto Benjol, funziona allo stesso yieldmodo, quindi dovrebbe quasi essere una variante di questo.


1
Un "wait" non deve necessariamente essere in un compito (anche se ovviamente in genere lo è.) È legale come operatore su praticamente qualsiasi espressione che abbia un tipo su cui possiamo trovare un GetAwaiter. (Le regole esatte devono ancora essere elaborate in una forma pubblicabile.)
Eric Lippert

1
@Eric, in F # sarebbe do!, ma lo sapevi che ...
Benjol,

1

yield async FetchAsync(..)


Questo si adatta perfettamente al asyncmodificatore necessario per applicare il metodo che stai invocando. E anche la semantica della corrente yield returncioè restituisci e restituisce l'esecuzione al codice di enumerazione mentre in questo caso stai cedendo la tua esecuzione al metodo asincrono.

Immagina se in futuro ci saranno altri usi per yield, potremmo aggiungere un yield xdove x è la nuova brillante funzionalità invece di avere tutte queste diverse parole chiave per fare principalmente la stessa cosa, dare esecuzione.

Francamente, non capisco del tutto l'argomento della "mancata esecuzione". Dopotutto, il punto di chiamare un altro metodo non è già quello di "dare esecuzione" a quel metodo? Indipendentemente dal fatto che sia asincrono o no? Mi sto perdendo qualcosa qui?

E bene per te se i asyncritorni in modo sincrono ma avendo la parola chiave lì dovrebbe significare che c'è una probabilità probabile che il metodo venga eseguito in modo asincrono e che tu cederai l'esecuzione a un altro metodo. Il tuo metodo dovrebbe tener conto di ciò indipendentemente dal fatto che il metodo esegua effettivamente chiamate asincrone o meno.

IMO Penso che i vari casi "non produttivi" siano un dettaglio di implementazione. Preferirei garantire coerenza nella lingua (ad es. Riutilizzo yield).


0

Che ne dici complete, come in "Voglio che l'attività sia completata"?

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

1
Perché il downvote? È una cortesia spiegare almeno te stesso dopo il downvoting.
Allon Guralnek,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.