web-dev-qa-db-fra.com

Dois-je attendre ReadAsStringAsync () si j'attends la réponse sur laquelle j'exécute ReadAsStringAsync ()?

Dois-je attendreReadAsStringAsync() si je attend la réponse sur laquelle j'exécute ReadAsStringAsync()? Pour clarifier davantage, quelle est la différence ou la bonne façon entre les éléments suivants? Sont-ils effectivement les mêmes?

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

OR

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

Votre premier exemple est le bon. Le deuxième exemple ne cède pas pendant l'opération asynchrone. Au lieu de cela, en obtenant la valeur de content.Result, vous forcez le thread actuel à attendre la fin de l'opération asynchrone.

En outre, comme le souligne le commentateur Scott Chamberlain, en bloquant le fil actuel, il est possible que vous introduisiez la possibilité d'un blocage. Cela dépend du contexte, mais un scénario courant pour await consiste à utiliser cette instruction dans le thread d'interface utilisateur, et le thread d'interface utilisateur doit rester réactif pour une variété de besoins, mais notamment pour pouvoir réellement gérer le achèvement d'une opération attendue.

Si vous évitez le deuxième modèle, c'est-à-dire la récupération de la valeur de la propriété Result à partir d'une Task dont vous ne savez pas qu'elle est terminée, vous pouvez non seulement garantir une utilisation efficace de vos threads, mais vous pouvez également assurer contre ce piège commun impasse.

21
Peter Duniho

La raison pour laquelle ReadAsString est une méthode async est que la lecture des données est en fait une opération IO. Le contenu peut ne pas être entièrement chargé même si vous ont déjà le résultat http. Il n'y a pas de threads supplémentaires ou de grandes charges de calcul impliqués.

HttpClient.GetAsync vous permet d'ajouter un HttpCompletionOption pour que le GetAsync ne revienne qu'une fois le HttpResult entièrement chargé. Dans ce cas, HttpContent.ReadAsStringAsync Se terminera de manière synchrone (un soi-disant chemin rapide) car le contenu est déjà là.

Vous devez donc l'attendre.

En outre: comme il s'agit probablement d'un code de bibliothèque qui ne dépend pas du retour au niveau du thread d'interface utilisateur, vous devez ajouter .ConfigureAwait(false) à tous les appels de méthode attendus.

9
Kai Brummund

Voici le code source .NET pour ReadAsStringAsync. Si vous regardez plus en profondeur dans la méthode LoadIntoBufferAsync (), vous verrez que cela continuera à lire le tampon de HttpResponse et conduira à un autre appel réseau potentiel. Cela signifie que c'est une bonne pratique d'utiliser attendre au lieu de Résultat.

[__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;
    }
6
Teoman shipahi