减少.net控制台应用程序中的线程数

f45qwnt8  于 12个月前  发布在  .NET
关注(0)|答案(2)|浏览(123)

问题

我有一个.Net7控制台应用程序,在多核Linux服务器上并行执行(同一程序的许多示例)。显然服务器有一个限制(ulimit -a),限制每个用户创建的线程数。如果应用程序想要创建更多线程,就会引发OutOfMemory异常(https://github.com/dotnet/runtime/issues/71761)。
如何重现问题(在Linux/wsl上):

for (var i = 0; i < int.Parse(args[0]); i++)
{
    var newThread = new Thread(() => Thread.Sleep(10_000));
    newThread.Start();
    if((i+1)%1000 == 0) Console.WriteLine($"Threads: {i+1}");
}
Console.WriteLine("ctrl+c to kill");

字符串
并运行

ulimit -u 4096


这将限制设置为4096。然后运行

dotnet run 5000


dotnet run 2500 & dotnet run 2500 &


代码将尝试在一个进程中创建5000个线程,或者在两个进程中创建5000个线程,但是系统在两种情况下都引发了4096作为limit => OutOfMemory。
考虑运行100个程序示例.如果程序尝试生成40个线程,则会引发OutOfMemory异常。

欲望

我们正试图增加这样的限制,但我想知道为什么这么多的线程产生(让我们使用#ObsTh = Process.GetCurrentProcess().Threads.Length).信息:

  • 它是一个.Net7控制台应用程序,以发布模式发布,linux-x64,独立
  • 该应用程序本身不会启动线程,它只是使用了casec/await范式。顶层语句中的第一行代码:#ObsTh=10 threads
  • 应用程序执行少量HttpClient.GetAsync()请求。HttpClient是从ServiceCollection中注册的IHttpClientFactory中检索的。#ObsTh=22个线程(+12)
  • 该应用使用StackExchange.Redis。连接后,#ObsTh=32个线程(+10)在一个专用线程池中。
  • 有时线程数达到35-40个线程,我的应用程序“基本上”是一个单线程应用程序。

不包括StackExchange.Redis(但如果你知道什么让我知道),我如何减少我的控制台应用程序中的线程数量?
只有几行代码显示了行为(我不包括redis):

using System.Diagnostics;
ThreadPool.SetMaxThreads(1, 1);

Console.WriteLine($"Start process: {Process.GetCurrentProcess().Threads.Count}");

HttpClient c = new HttpClient();
var result = await c.GetAsync("https://www.bing.com");
result.EnsureSuccessStatusCode();
Console.WriteLine($"After http call: {Process.GetCurrentProcess().Threads.Count}");


如果我用dotnet run运行这个program.cs(或者在发布运行之后),结果是:

Start process: 8
After http call: 22


所以...在httpcall 22之后,仅仅几行代码之后,我就有了8个线程。我只是想减少这个数字(如果可能的话),因为我正在运行这个程序的很多示例。

请记住:问题是Linux有一个限制,我必须减少我的应用程序使用的线程数量。在windows上没有问题,或者如果我增加linux限制。我的应用程序的一个示例现在运行大约30-40秒:

  • http调用所花费的时间:~0.5秒
  • 从redis中阅读数据所花费的时间:~3秒。
  • 剩下的时间是在CPU上。

Rider,使用上面的代码,显示:

  • 3未知?
  • 3 ThreadPool工作线程
  • 1线程池等待
  • 1个ThreadPool IO
  • 1 ThreadPool Gate
  • 1 .NET定时器
  • 1主线
  • 如何查看其他26-11=15个线程?更重要的是,如何减少它们?
cgyqldqp

cgyqldqp1#

其中大部分将是线程池线程,所以ThreadPool.SetMaxThreads是你的朋友。.NET历史上一直以桌面/服务器环境为目标,目的是使用可用的计算资源,所以针对受限的执行环境确实需要调整。

mnowg1ta

mnowg1ta2#

部分回应。
我无法使用ThreadPool.SetMaxThreads减少.net(和线程池)产生的线程。
可以将StackExchange.Redis配置为使用ThreadPool中的线程,而不是专用的线程池(由10个线程组成):

var configurationOptions = new ConfigurationOptions
    {
        SocketManager = SocketManager.ThreadPool
    };

字符串
所以,我可以减少8个线程。

相关问题