2015-01-28 12 views
12

Dovrei attendereReadAsStringAsync() se I è atteso la risposta su cui sto eseguendo ReadAsStringAsync()? Per chiarire ulteriormente, qual è la differenza o il modo giusto tra i seguenti? Sono effettivamente uguali?Devo attendere ReadAsStringAsync() se ho aspettato la risposta che sto eseguendo ReadAsStringAsync()?

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

O

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

penso che si dovrebbe fare solo 'contenuto var = response.Content.ReadAsStringAsync();' se si voleva fare qualcosa con il compito prima di utilizzare il risultato. –

+3

Non dovresti mai fare 'Map (content.Result)' puoi bloccare il tuo programma, se vuoi rimandarlo devi ancora usare await. 'Mappa (attendi il contenuto)', –

+0

Il tuo primo esempio è quello corretto in base alle risposte sottostanti, ma legge seriamente tutte le risposte, poiché spiegano perché l'esempio due può introdurre un deadlock. –

risposta

13

Il tuo primo esempio è quello corretto. Il secondo esempio non produce durante l'operazione asincrona. Invece, ottenendo il valore della proprietà content.Result, si forza il thread corrente ad attendere fino al completamento dell'operazione asincrona.

Inoltre, come osserva il commentatore Scott Chamberlain, bloccando il thread corrente è possibile introdurre la possibilità di un deadlock. Ciò dipende dal contesto, ma uno scenario comune per await consiste nell'utilizzare tale istruzione nel thread dell'interfaccia utente e il thread dell'interfaccia utente deve rimanere reattivo per una varietà di esigenze, ma deve essere in grado di gestire effettivamente il completamento di un'operazione attesa .

Se si evita il secondo schema, ovvero il recupero del valore della proprietà Result da un Task non noto è stato completato, non solo è possibile garantire un uso efficiente dei thread, è inoltre possibile garantire contro questa trappola deadlock comune .

+3

Come estensione dell'ultimo paragrafo, non vi è alcun costo aggiuntivo da attendere su un'attività già completata, sarà solo in modo sincrono restituire il risultato già disponibile. Se si è in un metodo che è già contrassegnato come 'async', non c'è motivo di chiamare mai' .Result' su 'await' su un'attività. –

8

Il motivo per cui ReadAsString è un metodo asincrono è che in realtà la lettura dei dati è un'operazione IO. Il contenuto potrebbe non essere completamente caricato anche se si dispone già del risultato http. Non ci sono thread aggiuntivi o grandi carichi di informatica coinvolti.

HttpClient.GetAsync consente di aggiungere un HttpCompletionOption per fare in modo che GetAsync ritorni solo dopo aver caricato l'intero HttpResult. In tal caso, HttpContent.ReadAsStringAsync verrà completato in modo sincrono (un cosiddetto percorso rapido) poiché il contenuto è già presente.

Quindi dovresti assolutamente aspettarlo.

Inoltre: poiché questo è probabilmente un codice di libreria che non dipende dal ritorno sul thread dell'interfaccia utente, è necessario aggiungere .ConfigureAwait (false) a tutte le chiamate di metodo attese.

+1

"Poiché questo è il codice della libreria" - come fai a sapere che questo è il codice della libreria? Mi sembra di aver perso quella parte nella domanda.I frammenti di codice sembrano poter essere semplicemente parte di un codice UI, in cui 'ConfigureAwait (false)' sarebbe esattamente la cosa sbagliata da fare. –

+1

@PeterDuniho ma vediamo tutto il codice tra il primo 'await' e l'istruzione return, sembra che non ci siano chiamate UI, quindi questo sarebbe un buon candidato per' ConfigureAwait (false) ' –

+2

@ScottChamberlain: probabilmente, ma senza contesto è difficile esserne sicuri. Per quanto ne sappiamo, l'OP ha semplicemente elidato le cose relative all'IU. La mia preoccupazione sarebbe che, aggiungendo 'ConfigureAwait (false)' per codificare dove non dovrebbe essere presente è molto peggio di non averlo nel codice dove dovrebbe essere presente. Cioè il secondo è semplicemente un'ottimizzazione (per un codice che è altrimenti corretto), mentre il primo potrebbe rompere qualcosa. –

2

Ecco il codice sorgente .NET per ReadAsStringAsync. Se si osserva più a fondo il metodo LoadIntoBufferAsync(), si vedrà che continuerà a leggere il buffer da HttpResponse e porterà a una potenziale ulteriore chiamata di rete. Ciò significa che è buona norma utilizzare attendere anziché Result.

[__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; 
    } 
Problemi correlati