使用适度冗长的操作在.NET Web Api方法中运行是否可行

时间:2013-12-05 10:33:42

标签: c# .net api task

我在Web API方法中重写了一篇关于异步任务的优秀文章,请参阅链接:

http://blog.tonysneed.com/2013/03/22/async-services-asp-net-web-api-entity-framework-6

现在我的解决方案(Framework 4.0)我有一个第三方工具,可以将URL转换为图像。我喜欢在Web Api方法中使用它。伪代码示例:

[HttpPost]
public HttpResponseMessage SaveSomething(...)
{
   SaveSomethingToDatabase(...);

   var image = ThirdPartyTool(someUrl);
   image.Save("someFullName");
}

因为第三方工具需要一些时间(平均3到6秒,Url调用加载Google地图图层)我想知道我是否应该将其嵌入到任务中(如文章所述,但第三方派对工具应该有一些它没有的异步支持)或Web Api是否足够可靠来处理这种情况(关于多个重复请求)?

更好的方法当然是创建专门用于创建图像的Windows服务。但后来我需要创建许多额外的东西(可靠的图像队列,Windows服务和安装程序等)。

2 个答案:

答案 0 :(得分:2)

首先,ASP.NET 4.0不支持asyncawait。完全停止。如果安装Microsoft.Bcl.Async,则可以将其转换为 compile ,但behavior at runtime is undefined。故事结束,对不起。


但即使有可能(例如,在升级到ASP.NET 4.5之后),您仍然不希望这样做。你绝对不能将它包装在Task

考虑当前的同步行为:

  • 请求进来; ASP.NET从其线程池中获取一个线程并将其分配给请求。
  • 同步处理请求。部分时间(在ThirdPartyTool方法内),请求线程被阻塞并空闲。

这不太理想,因为你确实有一个被阻止的请求线程没有做任何有用的事情。但是,ASP.NET旨在优雅地处理这种情况,因为它(现在)很常见。

现在,让我们将它包装在Task中。由于第三方工具完全同步,我们唯一的选择是Task.Run或同等选项:

[HttpPost]
public async Task<HttpResponseMessage> SaveSomething(...)
{
  SaveSomethingToDatabase(...);

  var image = await Task.Run(() => ThirdPartyTool(someUrl));
  image.Save("someFullName");
}

现在,考虑如何处理此异步请求:

  • 请求进来; ASP.NET从其线程池中获取一个线程A并将其分配给请求。
  • 当处理程序命中Task.Run时,它会将ThirdPartyTool排队到线程池。这将从ASP.NET的线程池中获取另一个线程B并开始执行代码。
  • 处理程序await是从Task.Run返回的任务。这释放了请求线程A,允许它返回到ASP.NET线程池。
  • 当第二个线程B完成ThirdPartyTool时,它会通知处理程序其任务已完成。然后处理程序执行Save(作为优化,处理程序的其余部分实际上在线程B上执行,而不是从线程池中拉出另一个线程,但这是一个实现细节。)

如果你考虑一下,那绝对是一种表现上的悲观情绪。你正在进行相同数量的工作,但也至少做了一次额外的上下文切换,并且通过意外地执行任务然后意外地在以后返回它来两次搞乱ASP.NET线程池启发式。

出于这个原因,避免在ASP.NET Task.Run是一个很好的指南。

Task.Run中包装代码就是我所谓的“假异步” - 它行为异步,看起来是异步的,但它下面只是将工作排队到线程池线程。

现在,如果你有一个真正的异步操作,那就是另一个故事了。假设第三方使用异步支持升级其工具,并且他们做得对。然后您的处理程序可能如下所示:

[HttpPost]
public async Task<HttpResponseMessage> SaveSomething(...)
{
  SaveSomethingToDatabase(...);

  var image = await ThirdPartyToolAsync(someUrl);
  image.Save("someFullName");
}

这次异步请求的处理完全不同:

  • 请求进来; ASP.NET从其线程池中获取一个线程并将其分配给请求。
  • ThirdPartyToolAsync向Google(或其他)发起异步网络请求。
  • 处理程序await是从ThirdPartyToolAsync返回的任务。这释放了请求线程,允许它返回到ASP.NET线程池。
  • 现在没有线程执行此请求。完全没有。
  • 稍后,Web请求(或其他)完成,ASP.NET从其线程池中获取一个线程以继续处理程序。
  • 该主题完成ThirdPartyToolAsync并继续完成SaveSomething

只有使用这样的真正异步方法才能从ASP.NET上获得async的任何好处,因为你减少线程池上的压力。虚假的异步方法增加压力,带来负面效益。

答案 1 :(得分:0)

是的,对于需要很长时间的请求,您应该使用async Task<T>await,其中T是您要返回的数据类型。

在等待外部工具操作完成时阻塞主线程对于性能是不可接受的。如链接文章中所述,如果以同步形式编写,Web API将最终生成线程。

实际上,.NET对此相对宽容 - 其他API(如Android 4.4)在尝试对主线程执行某些操作(例如HTTP请求)时会主动抛出异常。在这种情况下,执行此类操作的唯一方法(或至少是最好的方式)是使用Android AsyncTask