OK, ecco la mia opinione su di esso.
C'è qualcosa chiamato coroutine che è noto da decenni. ("Classe di Knuth e Hopper" "per decenni") Sono generalizzazioni di subroutine , in quanto non solo ottengono e rilasciano il controllo all'inizio della dichiarazione e del ritorno della funzione, ma lo fanno anche in punti specifici ( punti di sospensione ). Una subroutine è una coroutine senza punti di sospensione.
Sono FACILMENTE FACILI da implementare con le macro C, come mostrato nel seguente documento sui "protothreads". ( http://dunkels.com/adam/dunkels06protothreads.pdf ) Leggi. Aspetterò...
La linea di fondo è che le macro creano un grande switch
e case
un'etichetta in ogni punto di sospensione. Ad ogni punto di sospensione, la funzione memorizza il valore case
dell'etichetta immediatamente successiva , in modo che sappia dove riprendere l'esecuzione la prossima volta che viene chiamata. E restituisce il controllo al chiamante.
Questo viene fatto senza modificare il flusso apparente di controllo del codice descritto nel "protothread".
Immagina ora di avere un grande loop che chiama a turno tutti questi "protothreads" e che esegui contemporaneamente "protothreads" su un singolo thread.
Questo approccio presenta due inconvenienti:
- Non è possibile mantenere lo stato nelle variabili locali tra le riprese.
- Non è possibile sospendere il "protothread" da una profondità di chiamata arbitraria. (tutti i punti di sospensione devono essere al livello 0)
Esistono soluzioni alternative per entrambi:
- Tutte le variabili locali devono essere richiamate nel contesto del protothread (contesto che è già necessario per il fatto che il protothread deve memorizzare il prossimo punto di ripresa)
- Se ritieni di dover davvero chiamare un altro protothread da un protothread, "spawn" un protothread bambino e sospendi fino al completamento del bambino.
E se avessi il supporto del compilatore per eseguire il lavoro di riscrittura che fanno le macro e la soluzione alternativa, puoi semplicemente scrivere il tuo codice protothread proprio come intendi e inserire punti di sospensione con una parola chiave.
E questo è tutto async
e di che await
si tratta: creazione di coroutine (senza stack).
Le coroutine in C # sono reificate come oggetti della classe (generica o non generica) Task
.
Trovo queste parole chiave molto fuorvianti. La mia lettura mentale è:
async
come "sospettabile"
await
come "sospendi fino al completamento di"
Task
come "futuro ..."
Adesso. Dobbiamo davvero contrassegnare la funzione async
? Oltre a dire che dovrebbe innescare i meccanismi di riscrittura del codice per rendere la funzione un coroutine, risolve alcune ambiguità. Considera questo codice.
public Task<object> AmIACoroutine() {
var tcs = new TaskCompletionSource<object>();
return tcs.Task;
}
Supponendo che async
non sia obbligatorio, si tratta di una funzione di routine o normale? Il compilatore dovrebbe riscriverlo come coroutine o no? Entrambi potrebbero essere possibili con una diversa semantica finale.