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.