2013-06-22 8 views
10

Ich habe über asynchrone Funktionsaufrufe unter http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx?cs-save-lang=1&cs-lang=csharp gelesen.Wie unterscheidet sich async w/erwarten von einem synchronen Anruf?

Beim ersten Beispiel, sie tun dies, die ich erhalten:

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; 

Aber dann, dass sie erklären, wenn es gibt keine Arbeit in der Zwischenzeit getan werden, können Sie es wie folgt tun:

string urlContents = await client.GetStringAsync(); 

Von dem, was ich verstehe, wird das await Schlüsselwort den Code fließen, bis die Funktion zurückkehrt suspendieren. Also wie unterscheidet sich das von:

string urlContents = client.GetString(); 

?

+0

Es blockiert nicht den Hauptausführungskontext/Thread, also z.B. Die Benutzeroberfläche wird nicht eingefroren (in einer Anwendung, die die Benutzeroberfläche verwendet). –

+0

Aber das liegt hauptsächlich daran, dass die Methode selbst asynchron ist, oder? Wird das nicht so lange dauern, solange es mit Dingen beschäftigt ist, kann der aufrufende Thread fortfahren? – Rijk

+0

Nein, async wird benötigt, damit es abwarten kann. All die Magie passiert beim Warten auf den Anruf. – ghord

Antwort

8

Der Aufruf von await client.GetStringAsync() führt die Ausführung zur aufrufenden Methode aus, was bedeutet, dass nicht auf die Ausführung der Methode gewartet wird und somit der Thread nicht blockiert wird. Sobald die Ausführung im Hintergrund abgeschlossen ist, wird die Methode an der Stelle fortgesetzt, an der sie gestoppt wurde.

Wenn Sie einfach client.GetString() aufrufen, wird die Ausführung des Threads erst fortgesetzt, nachdem diese Methode beendet wurde. Dadurch wird der Thread blockiert und die Benutzeroberfläche reagiert möglicherweise nicht mehr.

Beispiel:

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

Ich habe es schließlich, wenn ich gelesen habe "Der Unterschied zwischen synchronen und asynchronen Verhalten ist, eine synchrone Methode zurückgibt, wenn ihre Arbeit abgeschlossen ist, aber eine asynchrone Methode _returns einen Aufgabenwert, wenn seine Arbeit ausgesetzt ist_.". Die asynchrone Funktion verhält sich also immer noch wie eine synchrone Funktion, bis sie eine 'await'-Anweisung ausgibt, die die Kontrolle an den Aufrufer zurückgibt. – Rijk

+1

@Rijk: Eine asynchrone Funktion ist nicht unbedingt mit 'async' Bezeichner gekennzeichnet - und gibt nicht unbedingt ein' await' aus. Eine normale Funktion, die eine 'Task' oder 'Task ' zurückgibt, kann als asynchrone Funktion betrachtet werden und Sie können darauf 'warten'. – YK1

8

Von dem, was ich verstehe, wird das await Schlüsselwort den Code Fluss auszusetzen, bis die Funktion gibt

Nun, Ja und Nein

  • Ja, weil der Code-Flow in gewissem Sinne aufhört.
  • Nein, weil der Thread, der diesen Codefluss ausführt, nicht blockiert. (Der synchrone Aufruf client.GetString() wird den Thread blockieren).

Tatsächlich wird es zu seiner Aufrufmethode zurückkehren. Um zu verstehen, was es durch die Rückkehr zu seiner Aufrufmethode bedeutet, können Sie über eine andere C# -Compiler Magie lesen - die yield return Anweisung.

Iterator Blöcke mit yield return wird die Methode in eine Zustandsmaschine brechen - wo Code nach der yield return Anweisung wird nur ausgeführt, nachdem MoveNext() auf den Enumerator aufgerufen wird. (Siehe this und this).

Nun, async/await Mechanismus basiert auch auf ähnlichen Zustand Maschine (jedoch ist es viel komplizierter als die yield return State Machine).

Zur Vereinfachung lässt eine einfache async Methode betrachten:

public async Task MyMethodAsync() 
{ 
    // code block 1 - code before await 

    // await stateement 
    var r = await SomeAwaitableMethodAsync(); 

    // code block 2 - code after await 
} 
  • Wenn Sie eine Methode mit async Kennung markieren Sie den Compiler sagen, das Verfahren in einer Zustandsmaschine zu brechen und dass Sie gehen zu await innerhalb dieser Methode.
  • Lässt sagen, Code läuft auf einem Thread Thread1 und Ihr Code ruft diese MyMethodAsync(). Dann wird code block 1 synchron auf demselben Thread ausgeführt.
  • SomeAwaitableMethodAsync() wird auch synchron aufgerufen - aber sagen wir, dass diese Methode eine neue asynchrone Operation startet und eine Task zurückgibt.
  • Dies ist, wenn await ins Bild kommt. Es wird den Code zurück zu seinem Aufrufer zurückgeben und der Thread Thread1 ist frei, Caller-Code zu laufen. Was dann in der Aufrufmethode passiert, hängt davon ab, ob die Methode await auf MyMethodAsync() aufruft oder etwas anderes tut - aber wichtig ist, dass Thread1 nicht blockiert ist.
  • Jetzt Rest der Magie warten - Wenn die Aufgabe von SomeAwaitableMethodAsync() schließlich abgeschlossen ist, ist die code block 2Geplant zu laufen.
  • async/await ist auf der Task parallel Bibliothek gebaut - so Scheduling ist über TPL getan.
  • Nun ist die Sache, dass diese code block 2 möglicherweise nicht über den gleichen Thread Thread1 geplant werden, es sei denn, es hatte eine aktive SynchronizationContext mit Thread-Affinität (wie WPF/WinForms UI-Thread). await ist SynchronizationContext bewusst, so dass code block 2 über dieselbe SynchronizationContext geplant ist, wenn überhaupt, wenn die MyMethodAsync() aufgerufen wurde. Wenn es keinen aktiven SynchronizationContext gab, dann wird mit aller Wahrscheinlichkeit code block 2 über einen anderen Thread laufen.

Schließlich will ich sagen, dass seit async/await auf Zustandsmaschine vom Compiler erstellt basiert, wie yield return es einige der Mängel teilt - zum Beispiel, können Sie nicht await innerhalb eines finally Block.

Ich hoffe, dass dies Ihre Zweifel löscht.

+0

Danke für die ausführliche Antwort! – Rijk

Verwandte Themen