Sfocare le linee tra le funzioni asincrone e quelle normali in C # 5.0


10

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 asyncla 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:

  1. Le funzioni asincrone non richiedono più la asyncdichiarazione. I loro tipi di ritorno non dovrebbero essere inclusi Task<>. Il compilatore identificherà una funzione asincrona durante la compilazione da solo e eseguirà il wrapping Task <> secondo necessità.

  2. 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.

  3. 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.

  4. 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).

  5. 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 awaitchiamata, è 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


3
Aspetti sempre le tue operazioni asincrone? Ti prego, dimmi che non lo fai subito dopo averli licenziati ...
Jimmy Hoffa,

1
Inoltre, una delle grandi cose di asincrono è che non devi farlo awaitimmediatamente. Puoi fare qualcosa del genere var task = FooAsync(); Bar(); await task;. Come lo farei nella tua proposta?
svick,

3
Quindi sta discutendo? Dov'è il mio BFG-3000 ...
Robert Harvey,

2
@talkol Pensi che la programmazione parallela sia oscena? È una prospettiva interessante, per non dire altro, quando parli 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.
svick

1
@svick haha, ora ci siamo andati :) Non penso che il prog parallelo sia osceno, ma penso che farlo con Async-waitit lo sia. Async-waitit è lo zucchero sintattico per mantenere il tuo stato d'animo sincrono senza pagare il prezzo del blocco. Se stai già pensando in parallelo, ti esorto a utilizzare uno schema diverso
talkol,

Risposte:


9

Alla tua domanda è già stata data risposta nella domanda SO che hai collegato.

Lo scopo di async / await è di rendere più semplice la scrittura di codice in un mondo con molte operazioni ad alta latenza. La stragrande maggioranza delle operazioni non ha un'elevata latenza.

Quando WinRT è uscito per la prima volta, i progettisti hanno descritto come hanno deciso quali operazioni sarebbero state asincrone. Decisero che tutto ciò che sarebbe durato 50ms o più sarebbe asincrono, e il resto dei metodi sarebbero stati metodi ordinari, non asincroni.

Quanti dei metodi hanno dovuto essere riscritti per renderli asincroni? Circa il 10 percento di loro. L'altro 90% non è stato affatto interessato.

Eric Lippert continua spiegando in modo abbastanza sostanziale i dettagli tecnici del motivo per cui hanno deciso di non adottare un approccio unico per tutti. Egli in sostanza dice che asynce awaitsono un'implementazione parziale di stile continuation che passa, e che l'ottimizzazione tale stile un per adattarsi a tutti i casi è un problema difficile.


Si noti la sostanziale differenza tra la domanda SO e questa. La SO chiede perché non rendere tutto asincrono. Qui non suggeriamo che, suggeriamo di rendere il 10% asincrono, usando solo la stessa sintassi per tutto questo. L'uso di una sintassi più stretta ha il vantaggio di poter modificare più facilmente quale 10% è asincrono, senza subire gli effetti domino di questi cambiamenti
talkol

Non sono ancora chiaro sul perché asyncproducano infestazioni di zombi. Anche se un metodo chiama altri 10 metodi, non devi semplicemente passare a asyncquello di livello superiore?
Robert Harvey,

6
Supponiamo che il 100% del mio codice attuale sia sincronizzato. Ora ho una singola funzione interna a livello foglia che interroga il DB che vorrei cambiare in asincrono. Ora per renderlo asincrono ho bisogno che il suo chiamante sia asincrono, e che il suo chiamante sia asincrono, e così via fino al livello più alto. Naturalmente sto parlando del caso in cui l'intera catena è attesa (per mantenere il design sincrono del codice o passare
valori di
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.