多个等待.Net WebApi中的异步

时间:2014-04-02 14:09:13

标签: c# asp.net-web-api async-await

我们有许多使用await / async关键字的异步控制器和服务。

许多动作看起来有点像:

public async Task<SomeViewModel> Get(int id)
{
    var someData = await _service.GetData(id);
    var someOtherData = await _service.GetMoreData(id);
    return new SomeViewModel
    {
        Data = someData,
        OtherData = someOtherData,
    }
}

可能是服务调用本身也有多个awaitawait通常会针对实体框架,服务总线或第三方Web端点的async调用。

我的一位同事今天提出这样的代码是毫无意义的,它只会为线程管理生成额外的工作,而在负载下,我们实际上会为运行时生成更多的工作并减慢应用程序的速度结果。

它们是否正确?如果是,那么当您在Web API请求中有多个IO绑定调用时,async / await的最佳做法是什么?

3 个答案:

答案 0 :(得分:10)

  

我的一位同事今天提出这样的代码是毫无意义的,它只会为线程管理生成额外的工作,而在负载下,我们实际上会为运行时生成更多的工作并减慢应用程序的速度结果。

这很有意思,因为事实恰恰相反。正如其他回答者所指出的那样,如果您使用的是真正的异步操作(即,不是Task.Run或类似的东西),那么使用更少的线程并且应用程序响应在负载下更好

有些人(不是我)已经对转换到async的“普通”ASP.NET应用程序进行了研究,他们发现在转移到async而不是阻塞时,可扩展性提高了10倍到100倍调用。如果您的应用程序有更多异步工作要做,您可以期望更好的可伸缩性。

如果您查看单个请求,并且每个操作一次完成一个,则异步版本稍微更慢。但是如果你把系统视为一个整体 - 特别是在负载下 - 异步版本可以更好地扩展。异步处理程序的另一个方面经常被忽略的是,异步版本对突发加载的响应比线程池本身的响应更快。

此外,异步代码可以轻松执行并发请求,这也可以使个别请求更快:

public async Task<SomeViewModel> Get(int id)
{
  var someDataTask = _service.GetData(id);
  var someOtherDataTask = _service.GetMoreData(id);
  await Task.WhenAll(someDataTask, someOtherDataTask);
  return new SomeViewModel
  {
    Data = await someDataTask,
    OtherData = await someOtherDataTask,
  }
}

答案 1 :(得分:2)

如果您在应用池中一次只提供一个请求,阻止解决方案将更有效。

如果你几乎有任何并行性,async / await可能会更高效,因为它会减少线程,减少上下文切换。因此,I / O绑定的工作负载(如果阻塞,很可能是上下文切换)实际上是async / await发挥最佳效果的地方之一。

正如boklucius所回答的,.NET中的异步I / O目标是I / O完成端口,它使用线程池不阻塞I / O但是处理I / O 完成。使用async肯定不会破坏你的线程数。

答案 2 :(得分:1)

您可以查看Async Performance: Understanding the Costs of Async and Await by Stephen Toub

I / O完成端口而不是多线程,切换影响应该可以忽略不计。一如既往,它取决于具体情况,如果有疑问的话。

我自己的经验表明,开销并不大,代码简单性也是值得的(我们的用例是一个tcp服务器,用于具有多个同时慢速客户端的自定义模糊协议)。它是对C ++ / threads / COM / win32应用程序的重写,使用大量await / async的.net版本在1/5行代码中的吞吐量是3倍,但正如我所说的那样。