如果我等待我正在执行ReadAsStringAsync()的响应,我应该等待ReadAsStringAsync()吗?

Tob*_*and 22 c# async-await dotnet-httpclient

我应该等待 ReadAsStringAsync(),如果我期待已久的上我在进行了响应ReadAsStringAsync()?为了进一步澄清,下列之间有什么不同或正确的方法?它们实际上是一样的吗?

var response = await httpClient.GetAsync("something");
var content = await response.Content.ReadAsStringAsync();
return new AvailableViewingTimesMapper().Map(content);
Run Code Online (Sandbox Code Playgroud)

要么

var response = await httpClient.GetAsync("something");
var content = response.Content.ReadAsStringAsync();
return new AvailableViewingTimesMapper().Map(content.Result);
Run Code Online (Sandbox Code Playgroud)

Pet*_*iho 22

你的第一个例子是正确的.第二个示例在异步操作期间不会产生.相反,通过获取content.Result属性的值,可以强制当前线程等待异步操作完成.

此外,正如评论者Scott Chamberlain指出的那样,通过阻止当前线程,你可能会引入死锁的可能性.这取决于上下文,但常见的情况await是在UI线程中使用该语句,并且UI线程需要保持响应以满足各种需求,但包括能够实际处理等待操作的完成.

如果你避免使用第二种模式,即Result从一个Task你不知道已经完成的属性中检索属性的值,你不仅可以确保有效地使用你的线程,还可以确保防止这种常见的死锁陷阱.

  • 作为最后一段的扩展,在已经完成的任务上等待没有额外的成本,它将同步返回已经可用的结果.如果你在一个已标记为"async"的方法中,没有理由在任务上调用`.Result`而不是`await`. (4认同)

Kai*_*und 9

ReadAsString一个async方法的原因是,实际读取数据是IO操作.即使您已经拥有http结果,内容也可能无法完全加载.没有额外的线程或涉及大量的计算.

HttpClient.GetAsync允许您添加一个,HttpCompletionOption以便GetAsync在整个HttpResult加载后获得唯一的返回.在这种情况下,HttpContent.ReadAsStringAsync将同步完成(所谓的快速路径),因为内容已经存在.

所以你绝对应该等待它.

另外:由于这可能是不依赖于在UI线程返回的库代码,因此您应该添加.ConfigureAwait(false)到所有等待的方法调用.

  • @roryap:`ConfigureAwait(false)`告诉任务它确实_not_需要在当前上下文中运行continuation.这适用于与UI代码无关的任何内容; 但是如果延续包括需要在当前上下文中执行的代码(即UI线程),例如访问某些UI对象,则调用`ConfigureAwait(false)`将导致继续在UI线程以外的线程上运行,结果是,当访问UI对象时,将抛出一个"InvalidOperationException"(或者类似的......那就是Winforms,我不记得WPF是我的头顶). (3认同)
  • “因为这是库代码”——你怎么知道这是库代码?我似乎错过了问题中的那部分。代码片段似乎很容易成为某些 UI 代码的一部分,其中 `ConfigureAwait(false)` 将是完全错误的做法。 (2认同)
  • @ScottChamberlain:可能,但没有上下文,很难确定.据我们所知,OP只是简化了与UI相关的东西.我担心的是,将`ConfigureAwait(false)`添加到不应该存在的代码中比在应该存在的代码中没有它更糟糕.即后者只是一个优化(对于其他方面正确的代码),而前者会破坏某些东西. (2认同)
  • @roryap:使用`Invoke()`可以解决这个问题,但是在这种情况下使用`await`的几乎全部都会被否定. (2认同)

Teo*_*ahi 6

这是ReadAsStringAsync的 .NET源代码.如果你深入研究LoadIntoBufferAsync()方法,你会看到这将继续从HttpResponse读取缓冲区并导致潜在的进一步网络调用.这意味着使用await而不是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;
    }
Run Code Online (Sandbox Code Playgroud)