47

我正在使用 HttpClient 将数据发布到 .NET 4.0 项目中的远程服务。我不关心这个操作阻塞,所以我想我可以跳过 ContinueWith 或 async/await 并使用 Result。

在调试时,我遇到了远程服务器没有响应的问题。当我单步执行代码时,我的代码似乎刚刚在第三行停止运行......当前堆栈指针行停止以黄色突出显示,并且没有前进到下一行。它就这样消失了。我花了一段时间才意识到我应该等待请求超时。

var client = new HttpClient();
var task = client.PostAsync("http://someservice/", someContent);
var response = task.Result;

我的理解是在 Task 上调用 Result 会导致代码同步执行,表现得更像这样(我知道 HttpClient 中没有 Post 方法):

var client = new HttpClient();
var response = client.Post("http://someservice/", someContent);

我不确定这是一件坏事,我只是想弄清楚它。由于 HttpClient 正在返回任务而不是直接返回结果这一事实,我的应用程序是否真的会自动利用异步,即使我认为我正在避免它?

4

2 回答 2

55

在 Windows 中,所有 I/O 都是异步的。同步 API 只是一种方便的抽象。

因此,当您使用 时HttpWebRequest.GetResponse,实际发生的是 I/O 启动(异步),调用线程(同步)阻塞,等待它完成。

同样,当您使用 时HttpClient.PostAsync(..).Result,I/O 会(异步)启动,调用线程(同步)会阻塞,等待它完成。

我通常建议人们使用await而不是Task.ResultTask.Wait出于以下原因:

  1. 如果你阻塞Task一个方法的结果async,你很容易陷入死锁情况
  2. Task.Result并将Task.Wait任何异常包装在一个中AggregateException(因为这些 API 是 TPL 的保留)。所以错误处理更加复杂。

但是,如果您知道这些限制,那么在某些情况下阻止 aTask可能很有用(例如,在控制台应用程序的 中Main)。

于 2012-09-18T20:32:25.393 回答
4

捕获任务的结果会阻塞当前线程。在这种情况下,使用异步版本的方法是没有意义的。Post()并且PostAsync().Result都会阻塞。

如果你想利用并发,你应该这样写:

async Task PostContent()
{
  var client = new HttpClient();
  Task t = await client.PostAsync("http://someservice/", someContent);
  //code after this line will execute when the PostAsync completes.
  return t;
}

由于PostContent()本身返回一个任务,调用它的方法也应该等待。

async void ProcessResult()
{
   var result = await PostContent(); 
   //Do work with the result when the result is ready 
}

例如,如果您调用ProcessResult()按钮单击处理程序,您会看到 UI 仍然是响应式的,其他控件仍然起作用。

于 2017-04-27T08:02:22.473 回答