gpt4 book ai didi

c# - 解决重负载下的线程池饥饿问题

转载 作者:行者123 更新时间:2023-12-05 04:23:49 25 4
gpt4 key购买 nike

我们的 dotnet-core (3.1) 应用程序遇到高负载问题。

超过一定数量的连接(虚拟用户),我们遇到了瓶颈,服务器被饿死,我们得到请求超时,但进程没有崩溃(没有 kestrel 日志)。我们正在使用 K6对我们的应用程序进行基准测试。目前,负载测试仅在登录页面上执行 GET 请求,这会在一个小数据集(无连接等)上触发一个基本的 SQL 请求。

我们使用 Visual Studio 2019 Perfomance Profiler 工具和 perfview 来调查这个问题,但这些工具都没有帮助我们识别导致此瓶颈的代码部分。

我找到了这篇关于线程池饥饿的文章:https://learn.microsoft.com/fr-fr/archive/blogs/vancem/diagnosing-net-core-threadpool-starvation-with-perfview-why-my-service-is-not-saturating-all-cores-or-seems-to-stall当我们使用任意值调整最小 ThreadPool 时,如后例所示,我们在性能上有了巨大的改进(不在图表上)。这似乎是一个权宜之计,使用它有多糟糕?

System.Threading.ThreadPool.SetMinThreads(200, 200);

benchmarks that show the starvation解释:2C_2G/100.csv => 2 核,2Go RAM,100 个虚拟用户

环境:

  • nginx 作为反向代理
  • K6作为基准工具
  • dotnet-core 3.1(带有 EntityFramework)
  • 操作系统:Ubuntu 20.04
  • mariadb 作为数据库

最佳答案

您正在线程池上执行长时间运行的代码。

下面是使用 Task.Run 执行此操作的方法:

public async Task<byte> CalculateChecksumAsync(Stream stream) => await Task.Run(() =>
{
int i;
byte checksum = 0;
while ((i = stream.ReadByte()) >= 0)
{
checksum += (byte)i;
}
return checksum;
});

对于看起来完全异步代码的不经意的观察者来说,因为有async/await 和 Task 无处不在。

但事实上,只要它需要,它就会占用一个线程池线程读取流(这不仅取决于通过的数据量,还取决于流的带宽)。

当线程池被饿死时,会延迟一秒线程池将产生一个新线程。这意味着随后调用Task.Run 会让他们的工作延迟那么久即使您的 CPU 闲置

备选方案:

  • 尽可能使用异步方法而不是同步方法(例如 Stream.ReadAsync),尤其是在线程池上时
  • 为长时间运行的代码生成长时间运行的任务:
    public async Task<byte> CalculateChecksumAsync(Stream stream) => await Task.Factory.StartNew(() =>
    {
    int i;
    byte checksum = 0;
    while ((i = stream.ReadByte()) >= 0)
    {
    checksum += (byte)i;
    }
    return checksum;
    },
    TaskCreationOptions.LongRunning);

TaskCreationOptions.LongRunning 标志告诉 C# 您需要一个新线程立即为您的工作生成。

关于c# - 解决重负载下的线程池饥饿问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73637676/

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