为什么要等待异步请求和读取其内容?

Kei*_*son 6 c# async-await c#-5.0 .net-4.5 asp.net-web-api

为什么.NET Web API中必须有一个异步读取HTTP响应内容的方法,因为已经存在一种异步发出请求的方法?换句话说,如果我正在使用HttpClient.GetAsync(或者PostAsync,PutAsync等等),那么代码是否真的更异步,同时也是异步读取内容?如果是这样,怎么/为什么?

这是:

using (var client = new HttpClient()) {
    var response = await client.GetAsync("...");
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsAsync<Foo>();
}
Run Code Online (Sandbox Code Playgroud)

比这更好:

using (var client = new HttpClient()) {
    var response = await client.GetAsync("...");
    response.EnsureSuccessStatusCode();
    return response.Content.ReadAsAsync<Foo>().Result;
}
Run Code Online (Sandbox Code Playgroud)

为什么?

Yuv*_*kov 7

为什么.NET Web API中必须有一个异步读取HTTP响应内容的方法,因为已经存在一种异步发出请求的方法?

因为它可以.这两种方法做了不同的事情.第一个通过线路发送网络请求,第二个从接收到的读取Stream.

由于发出网络请求和从流中读取都是IO绑定操作,因此您可以利用它们公开的异步api.

如果我正在使用HttpClient.GetAsync(或PostAsync,或PutAsync等),那么代码是否真的更异步,同时异步读取内容?如果是这样,怎么/为什么?

说一些事情"更加异步"听起来有点奇怪.但是,前一个例子比后者更为异步.

让我们尝试描绘以下场景:您创建一个负责与第三方服务交谈的库.你公开一个方法,让我们调用它FooAsync,它返回一个非常大的方法List<Foo>.用户知道API是异步的,所以他对从UI线程调用它感觉很好,并且不用担心它会长时间阻塞.

现在,让我们看看当他使用每种方法进行调用时发生了什么:

  1. 我们执行FooAsync并发出http请求,在到达第一个时向调用者提供控制await.然后,继续执行命中await从接收到的流中读取的第二个,再次将控制权返回给调用者,直到完成为止.一直以来,UI都可以自由处理消息,因为主要操作都是异步完成的.

  2. 我们执行FooAsync并发出http请求.我们控制第一await,一切都很好.然后,我们收到响应,并同步读取基础流.在我们阅读(并且让我们记住我们正在读取非常大的流)时,UI线程在IO操作上被阻止,无法处理任何其他消息.

我们的第二个案例绝对是不受欢迎的,更糟糕​​的是,意外.我们告诉用户他正在接收asyncapi,但我们最终会阻塞很长一段时间.不仅如此,如果Task.Result从UI线程调用,它可能会死锁.

await在两个操作上使用的好处是,您可以创建一个真正的异步api,而不是浪费任何时间来阻止IO操作,并且不会让任何通过阻塞调用您的方法的人感到惊讶.

我建议阅读异步编程中的最佳实践,其中讨论了所有这些边缘情况.

总而言之,如果你可以"一直"异步,那么就这样做.

  • 从内存中读取不是IO绑定操作.你的意思是"阅读来自网络的回复"吗? (2认同)