同步使用漂亮的.net 4.5 HttpClient的最佳方法

pm1*_*100 19 c# task-parallel-library

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

我需要代码(深入服务器内)

foo();
bar();
// compute stuff
var x = GetThingFromOtherServerViaHttp();
// compute more stuff
wiz(x);
Run Code Online (Sandbox Code Playgroud)

经典的顺序同步代码.我看到了几个类似的问题,但实际上从未真正说过"做这个".我在看

client.PostAsync.Wait()
Run Code Online (Sandbox Code Playgroud)

世界尖叫'不要这样做'.怎么样:

client.PostAsync.Result()
Run Code Online (Sandbox Code Playgroud)

这不只是伪装等待?

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

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

编辑:我有MS PM最近向我确认有一个任务'可能需要> X ms(我忘了​​X)的任何API必须是异步',许多PM将此解释为'仅异步'(不清楚这是什么旨在).因此Document DB api只是异步.

Jef*_*lop 9

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

return Task.Run(() => Client.PostAsync()).Result;
Run Code Online (Sandbox Code Playgroud)

  • 文章实际上说*不要*这样做,除非你绝对必须这样做.我不确定OP的使用是否符合文章中描述的想要执行此操作的狭窄路径. (4认同)
  • 那很好,给他留下评论,但"你不想这样做"真的不是答案.我确实以最糟糕的方式回答了他的问题. (4认同)
  • **不要这样做!**使用任务运行*异步*方法只是为了阻止它有什么意义?可以使用`var result = await Client.PostAsync();`或直接使用`Client.PostAsync().结果`来使用该方法.如果您担心等待可能会阻塞,例如因为UI被其他操作阻止,请使用`ConfigureAwait(false)`,即`var result = await Client.PostAsync().ConfigureAwait(false);`; (3认同)
  • @PanagiotisKanavos`Task.Run(()=> Client.PostAsync())`几乎就是`Client.PostAsync().ConfigureAwait(false)`.它确保将在ThreadPool中调度`Client.PostAsync()`的继续,而不是在初始调用的上下文中.`Task.Run`的作用是在异步调用中包装`Client`的异步调用,该异步调用将在ThreadPool中执行.因此,"Client"异步调用的继续将被调度到ThreadPool上下文.引入`ConfigureAwait`几乎是以支持框架的方式做同样的想法. (2认同)

nos*_*tio 7

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

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

这通常是UI应用程序的主线程(客户端),或随机ASP.NET线程处理HTTP请求(服务器端).在任何一种情况下,你真的不应该阻止它.

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

您提到"服务器内部深处",但没有提供有关服务器端应用程序的详细信息.大多数现代服务器端框架都有很好的管道async/await,所以它不应该是一个问题.

更新以解决评论:

我仍然不喜欢在不同地方编写的相同代码会死锁的事实.我希望在一个容易出现死锁的环境中等待调用

这不是特别的问题,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");
}
Run Code Online (Sandbox Code Playgroud)

从理论上讲,有可能尝试SynchronizationContext.Post通过检查来减轻内部潜在的死锁Thread.ThreadState == System.Threading.ThreadState.WaitSleepJoin.然而,这不是100%可靠的解决方案.