2015-08-08 10 views
17

Ho affermato la mia comprensione asincrona/attendo in un paio di occasioni, spesso discutendo se sia corretto o meno. Lo apprezzerei davvero se qualcuno potesse confermare o negare la mia comprensione e chiarire eventuali equivoci in modo da non diffondere informazioni errate.La mia comprensione dell'async/dell'attesa, di come funziona e dei suoi benefici, è corretta?

ad alto livello intesa

async/await è un modo per evitare di callback inferno durante la scrittura di codice asincrono. Un thread che sta eseguendo un metodo asincrono tornerà al pool di thread quando incontra uno await e prenderà l'esecuzione una volta completata l'operazione attesa.

basso livello intesa

Il JIT sarà dividere un metodi asincroni in parti discrete intorno await punti, permettendo il rientro nel metodo con lo stato del metodo conservato. Sotto le coperte si tratta di una sorta di macchina statale.

Relazione con il Concurrency

async/await non implica alcun tipo di concorrenza. Un'applicazione scritta usando async/await potrebbe essere interamente single-threaded mentre continua a raccogliere tutti i vantaggi, molto come node.js anche se con callback. A differenza di node.js, .NET è multithreading in modo da avere async/await, si ottengono i vantaggi dell'IO non bloccante senza utilizzare i callback, pur avendo più thread di esecuzione.

I benefici

async/await consente di liberare le discussioni per fare altre cose in attesa di IO per il completamento. Può anche essere utilizzato insieme allo TPL per eseguire il lavoro con CPU su più thread o fuori dal thread dell'interfaccia utente.

Per beneficiare da non bloccante IO, i metodi asincroni devono essere costruito su di API che effettivamente sfruttano non bloccante IO che vengono infine forniti dal sistema operativo.

Misuse

Questo è il più grande punto di contesa nella mia comprensione. Molte persone credono che il wrapping di un'operazione di blocco in un Task e l'utilizzo di async/await determineranno un aumento delle prestazioni. Creando un thread aggiuntivo per gestire un'operazione, restituendo il thread originale al pool di thread e quindi riprendendo il metodo originale dopo il completamento dell'attività, tutto ciò che si sta verificando sono interruttori di contesto non necessari mentre non si liberano realmente i thread per eseguire altre operazioni. Anche se questo non è tanto un uso improprio di async/await come è il TPL, questa mentalità sembra derivare da un malinteso di async/await.

+1

La tua comprensione è perfetta, corretta al 100%. –

+2

È il compilatore, piuttosto che il jitter, che crea la macchina a stati; non esiste qualcosa come "attendere" al livello sottostante. È possibile esaminare le classi prodotte se si utilizza ILSpy o simili. –

+0

Informazioni sull'uso improprio: se il tuo thread non ha nient'altro da fare, in effetti dovresti lasciargli il tuo thread. Buf se il tuo thread ha altre cose significative da fare, ad esempio mantieni la tua interfaccia utente reattiva, allora è saggio fare il lavoro pesante in un'attività separata –

risposta

13

Questo è praticamente corretto.

Qualche nota però:

  • Il filo che inizia l'esecuzione di un metodo asincrono è thread del chiamante che può, o non può, essere un filo ThreadPool.
  • Se viene raggiunta un'attesa, ma l'attendibile (in genere Task) è già completato, il thread continuerà a eseguire il resto del metodo in modo sincrono.
  • Il thread che riprende eseguendo il metodo è in genere un thread ThreadPool, ma quello dipende da SyncrhonizationContext e TaskScheduler.
  • Il JIT non è coinvolto qui (non più del solito). Il compilatore è quello che trasforma un metodo asincrono nella macchina a stati. Lo puoi vedere con this TryRoslyn example.
  • È vero che async-await non implica necessariamente la concorrenza in quanto potrebbe essere a thread singolo. Tuttavia, può essere comunque concorrente anche con un singolo thread avviando e attendendo contemporaneamente più operazioni asincrone.
  • async-await e TPL non sono parti completamente separate. async-await è costruito sopra il TPL. Ecco perché si chiama Pattern asincrono basato su attività.
  • Mentre la maggior parte delle operazioni veramente asincrone sono I/O, non tutte lo sono. Inoltre, di solito si ritardano in modo asincrono con Task.Delay o si utilizzano costrutti di sincronizzazione asincroni come SemaphoreSlim.WaitAsync.
+1

Wow, ottima risposta. Potresti chiarire una cosa? "può essere ancora concomitante anche con un singolo thread, avviando e attendendo contemporaneamente più operazioni asincrone." Com'è possibile? Ci sono thread che vengono generati al di fuori del runtime .NET? –

+1

@ w.brian "concurrent" qui non significa multi-thread. Significa che più operazioni vengono eseguite contemporaneamente (cioè simultaneamente). Ora se le tue operazioni sono asincrone e quindi non richiedono un thread, puoi avere operazioni simultanee con un solo thread. http://stackoverflow.com/a/10495458/885318 – i3arnon

+1

@ w.brian la concomitanza di un singolo thread è a livello di hardware I/O: un singolo thread può avviare (e attendere) più operazioni di I/O, mentre in modalità sincrona dovrebbe aspettare (mentre blocca) per ognuno di terminare prima di poter avviare quello successivo. –