【问题标题】:Multiple Await Async in .Net WebApi.Net WebApi 中的多个等待异步
【发布时间】:2014-05-13 21:28:11
【问题描述】:

我们有许多使用 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,
    }
}

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

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

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

【问题讨论】:

  • 这个问题已经有了很好的答案,但我想知道您的同事对开发 Web API 的团队有何看法。他们是否认为团队如此无能,以至于他们会开发一个功能并为用户增加额外的复杂性以使事情变慢?

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


【解决方案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 倍的吞吐量,但正如我所说,这取决于。

【讨论】:

    【解决方案2】:

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

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

    有些人(不是我)对“普通”ASP.NET 应用程序过渡到async 进行了研究,他们发现在迁移到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,
      }
    }
    

    【讨论】:

    • 感谢您展示如何重写代码以同时执行操作(只是担心我所有的异步调用都是顺序的)。对于使用 EF 检索多个数据集的人来说,值得注意的是单个上下文不允许这样做,因此顺序等待、等待位可能是您可以做的最好的事情,除非您想生成多个上下文(prolly 不应该)。
    【解决方案3】:

    如果您在应用程序池中一次只处理一个请求,阻塞解决方案会更有效。

    如果您几乎有任何并行性,async/await 可能会更有效,因为它会减少线程和上下文切换。正因为如此,I/O 密集型工作负载(如果阻塞则很有可能进行上下文切换)实际上是 async/await 表现最好的地方之一。

    正如 boklucius 所回答的,.NET 中的异步 I/O 以隐蔽的 I/O 完成端口为目标,它使用线程池不阻塞 I/O,而是处理 I/O完成。使用 async 绝对不会增加你的线程数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-13
      • 1970-01-01
      • 2018-12-31
      • 2014-02-15
      • 1970-01-01
      • 1970-01-01
      • 2015-05-22
      • 1970-01-01
      相关资源
      最近更新 更多