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

标签 c# nginx .net-core mariadb kestrel

我们的 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/

相关文章:

c# - 如何检测 Aero Peek 模式是否开启

c# - PdfSharp,在 C# 错误中更新元数据

c# - Redis 和 Siglnar 偶尔会在 EVAL 上挂起 SocketFailure

ssl - Nginx 允许密码 TLS_RSA_WITH_3DES_EDE_CBC_SHA

nginx - 验证 nginx 是否正在运行

c# - 将 List<T> 设置为 Null 的最佳实践

django - 如何优雅地重新启动在 nginx 后面运行 fcgi 的 django?

c# - Start.Process 不在单独的进程中运行脚本

windows - Jenkins Blue Ocean 工作区路径太长

c# - 单击一次不将 appsettings 安装到用户计算机