问题
我有一个.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个线程?更重要的是,如何减少它们?
2条答案
按热度按时间cgyqldqp1#
其中大部分将是线程池线程,所以
ThreadPool.SetMaxThreads
是你的朋友。.NET历史上一直以桌面/服务器环境为目标,目的是使用可用的计算资源,所以针对受限的执行环境确实需要调整。mnowg1ta2#
部分回应。
我无法使用
ThreadPool.SetMaxThreads
减少.net(和线程池)产生的线程。可以将
StackExchange.Redis
配置为使用ThreadPool中的线程,而不是专用的线程池(由10个线程组成):字符串
所以,我可以减少8个线程。