Ultimamente non riesco a ottenere abbastanza del fantastico schema di attesa asincrona di C # 5.0. Dove sei stato tutta la mia vita?
Sono assolutamente elettrizzato dalla semplice sintassi, ma ho una piccola difficoltà. Il mio problema è che le funzioni asincrone hanno una dichiarazione totalmente diversa dalle funzioni normali. Dal momento che solo le funzioni asincrone possono attendere su altre funzioni asincrone, quando sto provando a portare un vecchio codice di blocco su asincrono, sto avendo un effetto domino di funzioni che devo convertire.
Le persone si sono riferite a questa come un'infestazione di zombi . Quando async ottiene un morso nel tuo codice, continuerà a diventare sempre più grande. Il processo di porting non è difficile, sta semplicemente inserendo async
la dichiarazione e racchiudendo il valore restituito Task<>
. Ma è fastidioso farlo più volte durante il porting del vecchio codice sincrono.
Mi sembra che sarà molto più naturale se entrambi i tipi di funzione (asincrono e semplice vecchia sincronizzazione) avessero la stessa sintassi esatta. In tal caso, il porting richiederà zero sforzi e potrei passare indolore tra le due forme.
Penso che questo potrebbe funzionare se seguiamo queste regole:
Le funzioni asincrone non richiedono più la
async
dichiarazione. I loro tipi di ritorno non dovrebbero essere inclusiTask<>
. Il compilatore identificherà una funzione asincrona durante la compilazione da solo e eseguirà il wrapping Task <> secondo necessità.Niente più chiamate al fuoco e dimentica le funzioni asincrone. Se vuoi chiamare una funzione asincrona, dovrai attendere su di essa. Difficilmente uso il fuoco e dimentica comunque, e tutti gli esempi di pazze condizioni di gara o deadlock sembrano sempre basati su di essi. Penso che siano troppo confusi e "fuori dal mondo" con la mentalità sincrona che cerchiamo di sfruttare.
Se davvero non puoi vivere senza il fuoco e dimentica, ci sarà una sintassi speciale per questo. In ogni caso, non farà parte della semplice sintassi unificata di cui sto parlando.
L'unica parola chiave necessaria per indicare una chiamata asincrona è
await
. Se hai atteso, la chiamata è asincrona. Se non lo fai, la chiamata è semplicemente vecchia sincrona (ricorda, non abbiamo più il fuoco e dimentica).Il compilatore identificherà automaticamente le funzioni asincrone (poiché non hanno più una dichiarazione speciale). La Regola 4 rende tutto molto semplice: se una funzione contiene una
await
chiamata, è asincrona.
Potrebbe funzionare? Oppure mi sfugge qualcosa? Questa sintassi unificata è molto più fluida e potrebbe risolvere del tutto l'infestazione di zombi.
Qualche esempio:
// 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();
Nota: questa è la continuazione di un'interessante discussione correlata in SO
await
immediatamente. Puoi fare qualcosa del genere var task = FooAsync(); Bar(); await task;
. Come lo farei nella tua proposta?
async
. Penso che sia uno dei grandi vantaggi di async
- await
: che ti consenta di comporre facilmente operazioni asincrone (e non solo nel modo più semplice "avvia A, aspetta A, avvia B, aspetta B"). E c'è già una sintassi speciale proprio per questo scopo: si chiama await
.