2015-01-28 3 views
12

Sollte ich erwartenReadAsStringAsync() wenn ich erwartet die Antwort, auf der ich ReadAsStringAsync() durchführen? Um weiter zu klären, was ist der Unterschied oder der richtige Weg zwischen den folgenden? Sind sie tatsächlich gleich?Sollte ich auf ReadAsStringAsync() warten, wenn ich auf die Antwort wartete, die ich ReadAsStringAsync() ausführe?

var response = await httpClient.GetAsync("something"); 
var content = await response.Content.ReadAsStringAsync(); 
return new AvailableViewingTimesMapper().Map(content); 

ODER

var response = await httpClient.GetAsync("something"); 
var content = response.Content.ReadAsStringAsync(); 
return new AvailableViewingTimesMapper().Map(content.Result); 
+1

Ich glaube, Sie würden nur tun 'var content = response.Content.ReadAsStringAsync();', wenn Sie etwas mit der Aufgabe tun wollte, bevor Sie das Ergebnis verwenden. –

+3

Sie sollten nie 'Map (content.Result)' Sie können Ihr Programm Deadlock, wenn Sie es verzögern, müssen Sie immer noch warten. 'Map (erwarte Inhalt)', –

+0

Dein erstes Beispiel ist das richtige nach den unten stehenden Antworten, aber lese ernsthaft alle Antworten durch, da sie erklären, warum Beispiel zwei Deadlocks einführen kann. –

Antwort

13

Ihr erstes Beispiel ist das richtige. Das zweite Beispiel gibt während der asynchronen Operation nicht nach. Wenn Sie stattdessen den Wert der Eigenschaft content.Result abrufen, erzwingen Sie, dass der aktuelle Thread wartet, bis der asynchrone Vorgang abgeschlossen ist.

Darüber hinaus, wie Kommentator Scott Chamberlain hervorhebt, durch Blockieren des aktuellen Threads ist es möglich, dass Sie die Möglichkeit eines Deadlocks einführen könnten. Das hängt vom Kontext ab, aber ein gängiges Szenario für await besteht darin, diese Anweisung im UI-Thread zu verwenden, und der UI-Thread muss für eine Vielzahl von Anforderungen reaktionsfähig bleiben, aber auch, um den Abschluss einer erwarteten Operation tatsächlich verarbeiten zu können .

Wenn Sie das zweite Muster vermeiden, dh den Wert der Result Eigenschaft aus einer Task Abrufen Sie wissen nicht abgeschlossen ist, können Sie nicht nur die effiziente Nutzung der Threads gewährleisten, können Sie auch gegen diese gemeinsame Deadlock-Falle gewährleisten .

+3

Als eine Erweiterung des letzten Absatzes gibt es keine zusätzlichen Kosten, um auf eine bereits abgeschlossene Aufgabe zu warten, es wird nur synchron das bereits verfügbare Ergebnis zurückgeben. Wenn Sie sich in einer Methode befinden, die bereits als Async markiert ist, gibt es keinen Grund, '' Result' 'jemals '' '' 'auf einer Task zu nennen. –

8

Der Grund, warum ReadAsString ist ein Asynchron-Methode ist, die tatsächlich das Lesen von Daten ist ein IO-Betrieb. Der Inhalt ist möglicherweise nicht vollständig geladen, selbst wenn Sie bereits das http-Ergebnis haben. Es gibt keine zusätzlichen Threads oder große Rechenlasten.

HttpClient.GetAsync können Sie eine HttpCompletionOption hinzufügen, damit das GetAsync erst zurückkehrt, wenn das gesamte HttpResult geladen wurde. In diesem Fall wird HttpContent.ReadAsStringAsync synchron ausgeführt (ein sogenannter Fastpath), da der Inhalt bereits vorhanden ist.

So sollten Sie es definitiv erwarten.

Auch: Da dies wahrscheinlich Bibliothekscode ist, der nicht davon abhängig ist, dass der UI-Thread zurückgegeben wird, sollten Sie .ConfigureAwait (false) allen erwarteten Methodenaufrufen hinzufügen.

+1

"Da dies der Bibliothekscode ist" - woher wissen Sie, dass dies ein Bibliothekscode ist? Ich habe diesen Teil in der Frage übersehen.Die Codefragmente scheinen so zu sein, als könnten sie einfach Teil eines UI-Codes sein, wobei 'ConfigureAwait (false) 'genau das Falsche wäre. –

+1

@PeterDuniho aber wir sehen den ganzen Code zwischen der ersten 'erwarten' und der Rückkehranweisung, dort scheint es keine UI-Aufrufe zu geben, also wäre dies ein guter Kandidat für' ConfigureAwait (false) ' –

+2

@ScottChamberlain: wahrscheinlich, aber ohne Kontext ist es schwer sicher zu sein. Nach allem, was wir wissen, hat das OP einfach die UI-bezogenen Sachen weggelassen. Meine Sorge wäre, dass das Hinzufügen von 'ConfigureAwait (false)' zu Code, wo es nicht vorhanden sein sollte, viel schlimmer ist, als es in Code nicht zu haben, wo es vorhanden sein sollte. I.e. Letzteres ist einfach eine Optimierung (für Code, der ansonsten korrekt ist), während ersteres etwas kaputt machen würde. –

2

Hier ist .NET-Quellcode für ReadAsStringAsync. Wenn Sie tiefer in LoadIntoBufferAsync() -Methode schauen, werden Sie sehen, dies wird lesen Puffer aus HttpResponse und führt zu einem möglichen weiteren Netzwerkaufruf. Dies bedeutet, dass es eine gute Übung ist, anstelle des Ergebnisses zu verwenden.

[__DynamicallyInvokable] 
    public Task<string> ReadAsStringAsync() 
    { 
     this.CheckDisposed(); 
     TaskCompletionSource<string> tcs = new TaskCompletionSource<string>(); 
     HttpUtilities.ContinueWithStandard(this.LoadIntoBufferAsync(), (Action<Task>) (task => 
     { 
     if (HttpUtilities.HandleFaultsAndCancelation<string>(task, tcs)) 
      return; 
     if (this.bufferedContent.Length == 0L) 
     { 
      tcs.TrySetResult(string.Empty); 
     } 
     else 
     { 
      Encoding encoding1 = (Encoding) null; 
      int index = -1; 
      byte[] buffer = this.bufferedContent.GetBuffer(); 
      int dataLength = (int) this.bufferedContent.Length; 
      if (this.Headers.ContentType != null) 
      { 
      if (this.Headers.ContentType.CharSet != null) 
      { 
       try 
       { 
       encoding1 = Encoding.GetEncoding(this.Headers.ContentType.CharSet); 
       } 
       catch (ArgumentException ex) 
       { 
       tcs.TrySetException((Exception) new InvalidOperationException(SR.net_http_content_invalid_charset, (Exception) ex)); 
       return; 
       } 
      } 
      } 
      if (encoding1 == null) 
      { 
      foreach (Encoding encoding2 in HttpContent.EncodingsWithBom) 
      { 
       byte[] preamble = encoding2.GetPreamble(); 
       if (HttpContent.ByteArrayHasPrefix(buffer, dataLength, preamble)) 
       { 
       encoding1 = encoding2; 
       index = preamble.Length; 
       break; 
       } 
      } 
      } 
      Encoding encoding3 = encoding1 ?? HttpContent.DefaultStringEncoding; 
      if (index == -1) 
      { 
      byte[] preamble = encoding3.GetPreamble(); 
      index = !HttpContent.ByteArrayHasPrefix(buffer, dataLength, preamble) ? 0 : preamble.Length; 
      } 
      try 
      { 
      tcs.TrySetResult(encoding3.GetString(buffer, index, dataLength - index)); 
      } 
      catch (Exception ex) 
      { 
      tcs.TrySetException(ex); 
      } 
     } 
     })); 
     return tcs.Task; 
    }