我在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服务和安装程序等)。
答案 0 :(得分:2)
首先,ASP.NET 4.0不支持async
和await
。完全停止。如果安装Microsoft.Bcl.Async,则可以将其转换为 compile ,但behavior at runtime is undefined。故事结束,对不起。
但即使有可能(例如,在升级到ASP.NET 4.5之后),您仍然不希望这样做。你绝对不能将它包装在Task
。
考虑当前的同步行为:
ThirdPartyTool
方法内),请求线程被阻塞并空闲。这不太理想,因为你确实有一个被阻止的请求线程没有做任何有用的事情。但是,ASP.NET旨在优雅地处理这种情况,因为它(现在)很常见。
现在,让我们将它包装在Task
中。由于第三方工具完全同步,我们唯一的选择是Task.Run
或同等选项:
[HttpPost]
public async Task<HttpResponseMessage> SaveSomething(...)
{
SaveSomethingToDatabase(...);
var image = await Task.Run(() => ThirdPartyTool(someUrl));
image.Save("someFullName");
}
现在,考虑如何处理此异步请求:
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");
}
这次异步请求的处理完全不同:
ThirdPartyToolAsync
向Google(或其他)发起异步网络请求。await
是从ThirdPartyToolAsync
返回的任务。这释放了请求线程,允许它返回到ASP.NET线程池。ThirdPartyToolAsync
并继续完成SaveSomething
。只有使用这样的真正异步方法才能从ASP.NET上获得async
的任何好处,因为你减少线程池上的压力。虚假的异步方法增加压力,带来负面效益。
答案 1 :(得分:0)
是的,对于需要很长时间的请求,您应该使用async Task<T>
和await
,其中T是您要返回的数据类型。
在等待外部工具操作完成时阻塞主线程对于性能是不可接受的。如链接文章中所述,如果以同步形式编写,Web API将最终生成线程。
实际上,.NET对此相对宽容 - 其他API(如Android 4.4)在尝试对主线程执行某些操作(例如HTTP请求)时会主动抛出异常。在这种情况下,执行此类操作的唯一方法(或至少是最好的方式)是使用Android AsyncTask
。