Da quello che ho capito, la parola chiave attendono sospenderà il flusso di codice fino a quando la funzione restituisce
Beh, sì e no
- Sì, perché il flusso di codice si ferma in un certo senso.
- No, perché il thread in esecuzione questo flusso di codice non blocca. (La chiamata sincrona
client.GetString()
bloccherà il thread).
Infatti, tornerà al suo metodo di chiamata. Per capire cosa significa tornare al suo metodo di chiamata, puoi leggere su un'altra magia del compilatore C#: l'istruzione yield return
.
blocchi Iterator con yield return
si romperà il metodo in una macchina di stato - in cui il codice dopo l'istruzione yield return
eseguirà solo dopo MoveNext()
viene chiamato sul enumeratore. (Vedere this e this).
Ora, il meccanismo async/await
si basa anche su una macchina a stati simile (tuttavia, è molto più complicata della macchina a stati yield return
).
Per semplificare le cose, consente di prendere in considerazione un metodo semplice asincrono:
public async Task MyMethodAsync()
{
// code block 1 - code before await
// await stateement
var r = await SomeAwaitableMethodAsync();
// code block 2 - code after await
}
- Quando si contrassegna un metodo con
async
identificatore si dice al compilatore di rompere il metodo in una macchina statale e che si sta per await
all'interno di questo metodo.
- Diciamo che il codice è in esecuzione su un thread
Thread1
e il tuo codice chiama questo MyMethodAsync()
. Quindi code block 1
si eseguirà in modo sincrono sullo stesso thread.
SomeAwaitableMethodAsync()
verrà anche chiamato in modo sincrono - ma diciamo che il metodo avvia una nuova operazione asincrona e restituisce un valore Task
.
- Questo è quando
await
entra in figura. Restituirà il flusso del codice al suo chiamante e il thread Thread1
è libero di eseguire il codice del chiamante. Quello che succede poi nel metodo di chiamata dipende dal chiamare il metodo await
s su MyMethodAsync()
o fa qualcos'altro - ma la cosa importante è Thread1
non è bloccato.
- Ora resto della magia dell'attesa - Quando l'attività restituita da
SomeAwaitableMethodAsync()
termina, lo code block 2
è Programmato da eseguire.
async/await
è costruito sulla libreria parallela Task, quindi questa pianificazione viene eseguita su TPL.
- Ora il fatto è che questo
code block 2
potrebbe non essere programmato sulla stessa thread Thread1
a meno che non avesse uno SynchronizationContext
attivo con affinità di thread (come il thread dell'interfaccia utente WPF/WinForms). await
è SynchronizationContext
consapevole, quindi code block 2
è programmato per lo stesso SynchronizationContext
, se presente, quando è stato chiamato il numero MyMethodAsync()
. Se non era attivo SynchronizationContext
, quindi con tutte le possibilità, code block 2
verrà eseguito su thread diversi.
Infine, devo dire che da quando async/await
si basa sulla macchina statale creato da compilatore, come yield return
, condivide alcune delle carenze - per esempio, non è possibile await
all'interno di un finally
blocco.
Spero che questo chiarisca i tuoi dubbi.
Non blocca il contesto/thread di esecuzione principale, quindi ad es. L'interfaccia utente non verrà congelata (in un'applicazione che utilizza l'interfaccia utente). –
Ma questo è principalmente dovuto al fatto che il metodo stesso è 'async', giusto? Ciò non significa che finché è occupato con roba, il thread chiamante può continuare? – Rijk
No, è necessario async per poter richiamare l'attesa. Tutta la magia si svolge nell'attesa della chiamata. – ghord