2013-06-22 12 views
10

Stavo leggendo le chiamate di funzioni asincrone su http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx?cs-save-lang=1&cs-lang=csharp.In che modo asincrona w/attende diversi da una chiamata sincrona?

Al primo esempio, lo fanno, che ottengo:

Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); 

// You can do work here that doesn't rely on the string from GetStringAsync. 
DoIndependentWork(); 

string urlContents = await getStringTask; 

Ma poi spiegare che se non c'è alcun lavoro da fare nel frattempo, si può semplicemente fare in questo modo:

string urlContents = await client.GetStringAsync(); 

Da quello che ho capito, la parola chiave await sospenderà il flusso di codice fino a quando la funzione ritorna. Quindi, come è diverso da:

string urlContents = client.GetString(); 

?

+0

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

+0

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

+0

No, è necessario async per poter richiamare l'attesa. Tutta la magia si svolge nell'attesa della chiamata. – ghord

risposta

8

Chiamando await client.GetStringAsync() restituisce l'esecuzione al metodo di chiamata, il che significa che non aspetterà che il metodo finisca di essere eseguito, e quindi non bloccherà il thread. Una volta terminato l'esecuzione in background, il metodo continuerà da dove si è fermato.

Se si chiama semplicemente client.GetString(), l'esecuzione del thread non continuerà fino a quando questo metodo non avrà completato l'esecuzione, il che bloccherà il thread e potrebbe causare la mancata risposta dell'interfaccia utente.

Esempio:

public void MainFunc() 
{ 
    InnerFunc(); 
    Console.WriteLine("InnerFunc finished"); 
} 

public void InnerFunc() 
{ 
    // This causes InnerFunc to return execution to MainFunc, 
    // which will display "InnerFunc finished" immediately. 
    string urlContents = await client.GetStringAsync(); 

    // Do stuff with urlContents 
} 

public void InnerFunc() 
{ 
    // "InnerFunc finished" will only be displayed when InnerFunc returns, 
    // which may take a while since GetString is a costly call. 
    string urlContents = client.GetString(); 

    // Do stuff with urlContents 
} 
+2

Ho finalmente capito quando ho letto "La differenza tra il comportamento sincrono e asincrono è, un metodo sincrono restituisce quando il suo lavoro è completo, ma un metodo asincrono _ restituisce un valore di attività quando il suo lavoro è sospeso_.". Quindi la funzione asincrona si comporterà ancora come le funzioni sincrone, * fino a * emetteranno un'istruzione 'await', che restituirà il controllo al chiamante. – Rijk

+1

@Rijk: una funzione asincrona non è necessariamente contrassegnata con l'identificatore 'async' - e non necessariamente emette un' await'. Una funzione normale che restituisce un 'Task' o' Task 'può essere considerata come funzione asincrona e si può' attendere 'su di essa. – YK1

8

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.

+0

Grazie per l'elaborata risposta! – Rijk

Problemi correlati