Art*_*Art 2 c# performance async-await asp.net-web-api asp.net-core
我在理解 REST API 中使用 async/await 的优点时遇到问题。\n我有这个 CPU 密集型任务:
\n [HttpGet("GetHeavyStuffAsync")]\n public async Task<string> GetHeavyStuffAsync()\n {\n Guid id = Guid.NewGuid();\n System.Diagnostics.Debug.WriteLine($"{id} has started");\n await _expensiveOperations.DoHeavyStuffOnDifferentThread();\n System.Diagnostics.Debug.WriteLine($"{id} has finished");\n return "request processed";\n }\nRun Code Online (Sandbox Code Playgroud)\n这是在哪里DoHeavyStuffOnDifferentThread():
public async Task DoHeavyStuffOnDifferentThread()\n {\n var t = Task.Run(() =>\n {\n var limit = 4000;\n var array = Enumerable.Range(0, limit).ToArray();\n stoogesort(array, 0, array.Count() - 1);\n });\n\n await t;\n }\nRun Code Online (Sandbox Code Playgroud)\n我使用Task.Run(() => ...的是 CPU 繁重的东西在不同的威胁中执行而不阻塞主要的威胁,并使用 async/await 希望控制器线程不会被繁重的任务阻塞并且可以继续处理请求。
为了测试我编写了一个程序,该程序针对 启动了 250 个请求GetHeavyStuffAsync(),之后我从 swagger 向同一 API 控制器中的不同端点发出了请求:
[HttpGet]\n public IEnumerable<WeatherForecast> Get()\n {\n System.Diagnostics.Debug.WriteLine("GETTING FORECAST ...");\n var rng = new Random();\n return Enumerable.Range(1, 5).Select(index => new WeatherForecast\n {\n Date = DateTime.Now.AddDays(index),\n TemperatureC = rng.Next(-20, 55),\n Summary = Summaries[rng.Next(Summaries.Length)]\n })\n .ToArray();\n }\nRun Code Online (Sandbox Code Playgroud)\n正如您所看到的,最后一个端点是您创建 API 项目时 Visual Studio 默认创建的端点,它是一个非常简单的函数,会立即返回。
\n我期望发生的情况:对的调用GetHeavyStuffAsync将被处理,await _expensiveOperations.DoHeavyStuffOnDifferentThread();控制权将传递给执行繁重工作的线程,API 控制器将可以自由地处理其他请求,在某个时刻DoHeavyStuffOnDifferentThread将完成,控制器将继续这条指令System.Diagnostics.Debug.WriteLine($"{id} has finished");完成该方法后,就可以自由地继续处理其他请求。
实际发生的情况是public IEnumerable<WeatherForecast> Get()几分钟后才返回。
那么,如果我没有\xc2\xb4t 使用不同的线程或异步/等待,那么为什么行为与我的行为没有区别呢?
\n(注:测试期间我的笔记本电脑的CPU和内存剩余在50%左右)
\n我正在使用 Task.Run(() => ... 以便 cpu 重的东西在不同的威胁中执行,而不会阻塞主要的威胁
在 ASP.NET 中这样做没有任何好处。
在桌面应用程序中,UI 只能由主线程更新。因此,将 CPU 密集型任务卸载到另一个线程是有意义的,因为它可以释放 UI 线程以继续响应用户输入。
然而,在 ASP.NET 中,没有一个“主线程”。每个新请求都会分配一个新线程,直到达到最大线程池计数,然后任何进一步的请求都必须等待。
因此,当您使用 时Task.Run,您将释放主请求的线程,但您正在使用另一个线程。因此,对线程池计数的净影响仍然相同。
在ASP.NET Core 性能最佳实践一文中,Microsoft 建议:
不要:
- 调用 Task.Run 并立即等待它。ASP.NET Core 已经在普通线程池线程上运行应用程序代码,因此调用 Task.Run 只会导致额外不必要的线程池调度。即使计划的代码会阻塞线程,Task.Run 也不会阻止这种情况。
异步代码仅在发出 I/O 请求(网络、文件系统等)时对您有帮助,因为在等待期间实际上无事可做。但对于 CPU 密集型任务来说,没有任何好处。
| 归档时间: |
|
| 查看次数: |
963 次 |
| 最近记录: |