gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 07:45:10 29 4
gpt4 key购买 nike

我正在尝试对几个 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 的解释,我期望异步端点的性能更高,因为它会立即将线程池线程释放回线程池,从而使它们可用于其他请求并提高吞吐量。但是数字看起来完全一样。

我在这里误解了什么?

最佳答案

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

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

  • Extra (unnecessary) thread switching to the Task.Run thread pool thread. Similarly, when that thread finishes the request, it has to enter the request context (which is not an actual thread switch but does have overhead).
  • Extra (unnecessary) garbage is created. Asynchronous programming is a tradeoff: you get increased responsiveness at the expense of higher memory usage. In this case, you end up creating more garbage for the asynchronous operations that is totally unnecessary.
  • The ASP.NET thread pool heuristics are thrown off by Task.Run “unexpectedly” borrowing a thread pool thread. I don’t have a lot of experience here, but my gut instinct tells me that the heuristics should recover well if the unexpected task is really short and would not handle it as elegantly if the unexpected task lasts more than two seconds.
  • ASP.NET is not able to terminate the request early, i.e., if the client disconnects or the request times out. In the synchronous case, ASP.NET knew the request thread and could abort it. In the asynchronous case, ASP.NET is not aware that the secondary thread pool thread is “for” that request. It is possible to fix this by using cancellation tokens, but that’s outside the scope of this blog post.

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

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

如何让它真正异步:

GetNewsFeedItemsForUser 方法应转换为 async

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

这样做:

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

关于c# - 具有 Task.Run 性能的 ASP.NET Web API 2 异步操作方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29168188/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com