ASP.NET Web API 2具有Task.Run性能的异步操作方法

时间:2015-03-20 13:54:13

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

我正在尝试使用几个ASP.NET Web API 2.0端点进行基准测试(使用Apache工作台)。其中一个是同步和一个异步。

        [Route("user/{userId}/feeds")]
        [HttpGet]
        public IEnumerable<NewsFeedItem> GetNewsFeedItemsForUser(string userId)
        {
            return _newsFeedService.GetNewsFeedItemsForUser(userId);
        }

        [Route("user/{userId}/feeds/async")]
        [HttpGet]
        public async Task<IEnumerable<NewsFeedItem>> GetNewsFeedItemsForUserAsync(string userId)
        {
            return await Task.Run(() => _newsFeedService.GetNewsFeedItemsForUser(userId));
        }

观看Steve Sanderson's presentation后,我向每个端点发出了以下命令ab -n 100 -c 10 http://localhost....

我很惊讶,因为每个端点的基准测试似乎大致相同。

关闭Steve所解释的我期望异步端点的性能更高,因为它会立即将线程池线程释放回线程池,从而使它们可用于其他请求并提高吞吐量。但数字似乎完全相同。

我在这里误解了什么?

1 个答案:

答案 0 :(得分:18)

使用await Task.Run创建“async” WebApi是一个坏主意 - 您仍然会使用线程,甚至来自the same thread pool used for requests

这会导致一些不愉快的时刻,详细描述here

  
      
  • 额外(不必要的)线程切换到Task.Run线程池线程。同样,当该线程完成请求时,它必须   输入请求上下文(不是实际的线程切换但是   确实有开销)。
  •   
  • 创建额外(不必要的)垃圾。异步编程是一种权衡:你以更高的代价获得更高的响应能力   内存使用情况。在这种情况下,您最终会为此创建更多垃圾   异步操作完全没必要。
  •   
  • ASP.NET线程池启发式被Task.Run“意外地”借用线程池线程抛出。我没有很多   在这里体验,但我的直觉告诉我启发式   如果意外的任务真的很短,那么应该恢复得好   如果意外任务持续超过两个,则不能优雅地处理它   秒。
  •   
  • ASP.NET无法提前终止请求,即,如果客户端断开连接或请求超时。在同步的情况下,   ASP.NET知道请求线程并可以中止它。在里面   在异步的情况下,ASP.NET不知道辅助线程池   线程是“为”该请求。可以通过使用来解决这个问题   取消令牌,但这不在本博文的范围内。
  •   

基本上,您不允许任何与ASP.NET的异步 - 您只需隐藏异步外观后面的CPU绑定同步代码。 Async本身是I / O绑定代码的理想选择,因为它允许以最高效率利用CPU(线程)(不阻塞I / O),但是当你有计算绑定代码时,你将仍然必须在相同程度上利用CPU。

考虑到Task和上下文切换的额外开销,您将获得比使用简单同步控制器方法更糟糕的结果。

如何使其成为真正的ASYNC:

GetNewsFeedItemsForUser方法应转为async

    [Route("user/{userId}/feeds/async")]
    [HttpGet]
    public async Task<IEnumerable<NewsFeedItem>> GetNewsFeedItemsForUserAsync(string userId)
    {
        return await _newsFeedService.GetNewsFeedItemsForUser(userId);
    }

要做到这一点:

  • 如果它是某种库方法,那么寻找它的async变体(如果没有 - 运气不好,你将不得不搜索一些竞争模拟)。
  • 如果是使用文件系统或数据库的自定义方法,则利用其异步工具为该方法创建异步API。