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

本文关键字:HttpClient 最佳 方式 net 同步 | 更新日期: 2023-09-27 18:07:36

我喜欢新的System.Net.Http.HttpClient类。它有一个很简单的API,它不会抛出正常的错误。

我需要的代码(在服务器的深处)

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

经典顺序同步代码。我看到了几个类似的SO问题,但实际上从来没有说过"做这个"。我看了

client.PostAsync.Wait()

全世界都在尖叫"不要这样做"。如何:

client.PostAsync.Result()

这不是伪装的等待吗?

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

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

编辑:我最近有MS PM向我确认,有一个命令"任何可能需要> X MS(我忘了X)的API必须是异步的",许多PM将其解释为"仅异步"(不清楚这是否是什么意思)。因此Document DB api只能async。

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

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

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

@Noseratio -当然我关心死锁:-)

只有当你在一个具有同步上下文的线程上时,你才会关心死锁

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

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

你提到"服务器内部深处",但没有详细说明这是哪种服务器端应用程序。大多数现代服务器端框架都有很好的async/await管道,所以接受它应该不是问题。

更新以解决注释:

我仍然不喜欢用不同的语言写相同的代码地方将陷入僵局。我认为在容易死锁的情况下应该使用wait调用抛出

的环境

这不是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稍微轻一些。

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