Ultimamente non riesco a ottenere abbastanza del sorprendente async-await modello di C # 5.0. Dove sei stata tutta la mia vita?
Sono assolutamente entusiasta della semplice sintassi, ma sto riscontrando una piccola difficoltà. Il mio problema è che le funzioni asincrone hanno una dichiarazione totalmente diversa dalle normali funzioni. Poiché solo le funzioni asincrone possono attendere su altre funzioni asincrone, quando sto provando a portare un vecchio codice di blocco su async, sto avendo un effetto domino di funzioni che devo convertire.
Le persone si sono riferite a questa come infestazione zombi . Quando async prende un morso nel tuo codice, continuerà a diventare sempre più grande. Il processo di porting non è difficile, è solo il lancio di async
nella dichiarazione e il ritorno del valore di ritorno con Task<>
. Ma è fastidioso farlo più e più volte durante il porting del vecchio codice sincrono.
Mi sembra molto più naturale se entrambi i tipi di funzione (sincrona asincrona e semplice) abbiano la stessa sintassi esatta. In questo caso, il porting richiederà uno sforzo zero e potrei passare senza problemi tra le due forme.
Penso che questo potrebbe funzionare se seguiamo queste regole:
-
Le funzioni asincrone non richiedono più la dichiarazione
async
. I loro tipi di ritorno non dovrebbero essere racchiusi inTask<>
. Il compilatore identificherà una funzione asincrona durante la compilazione da solo ed eseguirà l'operazione < > riavvolgimento automatico in base alle esigenze. -
Niente più chiamate ignote alle funzioni asincrone. Se si desidera chiamare una funzione asincrona, è necessario attendere su di essa. Uso a malapena il fuoco e lo dimentico comunque, e tutti gli esempi di pazze condizioni di gara o di situazioni di stallo sembrano sempre basate su di loro. Penso che siano troppo confusi e "fuori controllo" con la mentalità sincrona che cerchiamo di sfruttare.
-
Se davvero non puoi vivere senza fire-and-forget, 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 che devi indicare una chiamata asincrona è
await
. Se hai atteso, la chiamata è asincrona. Se non lo fai, la chiamata è semplice sincrona (ricorda, non abbiamo più fire-and-forget). -
Il compilatore identificherà automaticamente le funzioni asincrone (dato che non hanno più una dichiarazione speciale). La regola 4 rende questo molto semplice da fare - se una funzione ha una chiamata
await
all'interno, è asincrona.
Potrebbe funzionare? Oppure mi sfugge qualcosa? Questa sintassi unificata è molto più fluida e potrebbe risolvere completamente l'infestazione di zombi.
Alcuni esempi:
// 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