同步使用 nice .net 4.5 HttpClient 的最佳方式

best way to use the nice .net 4.5 HttpClient synchronously

我喜欢新的 System.Net.Http.HttpClient class。它有一个很好的简单 API,它不会抛出正常错误。但它只是异步的。

我需要运行的代码(深入服务器)

foo();
bar();
// compute stuff
var x = GetThingFromOtherServerViaHttp();
// compute more stuff
wiz(x);

classic 顺序同步代码。我看到了几个类似的 SO 问题,但实际上并没有说 'do this'。我看了

client.PostAsync.Wait()

全世界都在尖叫'dont do this'。怎么样:

client.PostAsync.Result()

这不就是变相的等待吗?

最后,我最终传递了一个处理结果的 lambda 回调,然后唤醒了显式等待 EventWaitHandle 的调用线程。很多管道。有没有更简单的东西,或者我应该回去使用旧的 http 客户端

编辑:进一步阅读后,我怀疑这段代码与 Wait 和 Result 有相同的问题,它只是一个更冗长的死锁

编辑:我最近让 MS PM 向我确认有一个授权 'any API that might take > X ms (I forgot X) must be async',许多 PM 将其解释为 'async only'(不清楚这是否是预期的)。因此文档数据库 api 只是异步的。

来自http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx

return Task.Run(() => Client.PostAsync()).Result;

@Noseratio - of course I am concerned about deadlocks :-)

如果您在具有同步上下文的线程上,您只会担心死锁

这通常是 UI 应用程序(客户端)的主线程,或者是处理 HTTP 请求(服务器端)的随机 ASP.NET 线程。无论哪种情况,您都不应该阻止它。

接受的答案可能有助于缓解僵局,但像这样阻止仍然会损害您的 UI 应用程序的最终用户体验(UI 将被冻结)或服务器端应用程序可扩展性(一个线程因阻塞而浪费,而它可能正在为另一个请求服务)。只是不要阻止并一直使用 async/await

您提到了 "deep inside a server" 但没有提供有关服务器端应用程序类型的详细信息。大多数现代服务器端框架都为 async/await 提供了良好的管道,因此采用它应该不是问题。

已更新 以解决评论:

I still dont like the fact that the same code written in different places will deadlock. I would expect the wait call in a deadlock-prone environment to throw

这不是 async/await 的特别问题,而是一般的同步上下文概念的问题,当它与阻塞代码一起使用时。死锁简述如下:

private void Form1_Load(object sender, EventArgs e)
{
    var mre = new System.Threading.ManualResetEvent(initialState: false);
    System.Threading.SynchronizationContext.Current.Post(_ => 
        mre.Set(), null);
    mre.WaitOne();
    MessageBox.Show("We never get here");
}

理论上,可以尝试减轻 SynchronizationContext.Post 内部潜在的死锁,比如检查 Thread.ThreadState == System.Threading.ThreadState.WaitSleepJoin。然而,这不是 100% 可靠的解决方案。

有争议的是,我要说 System.Net.Http.HttpClient 可能 只需使用 .Result

Microsoft 应该 遵循他们自己的建议并在该库中一直使用 .ConfigureAwait(false)。参考来源暗示:

https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpClient.cs

为什么要冒险?因为它应该比Task.Run(() => Client.PostAsync()).Result.

轻一点

我不相信许多(任何?)其他库可以安全编码,并期待看到是否有人反对我的回答或更好但证明我错了。