提问人:Sierrodc 提问时间:10/31/2023 最后编辑:Sierrodc 更新时间:11/7/2023 访问量:63
减少 .net 控制台应用程序中的线程数
Reduce number of threads in .net console application
问:
问题
我有一个.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,独立
- 该应用程序本身不会启动线程,它只是使用 async/await 范式。顶级语句中的第一行代码:#ObsTh=10 个线程
- 应用执行少量 HttpClient.GetAsync() 请求。从 中注册的 中检索。#ObsTh=22 个线程 (+12)
HttpClient
IHttpClientFactory
ServiceCollection
- 该应用程序使用 .连接后,#ObsTh=专用线程池中的 32 个线程 (+10)。
StackExchange.Redis
- 有时线程数达到 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}");
如果我在发布运行(或发布运行后)运行此程序.cs,结果是:dotnet run
Start process: 8
After http call: 22
所以。。。在几行代码之后,我有 8 个线程,在 httpcall 22 之后。我只想减少这个数字(如果可能的话),因为我正在运行这个程序的大量实例。
请记住:问题是 linux 有一个限制,我必须减少我的应用程序使用的线程数量。 在 Windows 上没有问题,或者如果我增加 linux 限制。 我的应用程序的一个实例现在运行大约 30-40 秒:
- http 调用花费的时间:~0.5 秒
- 从 redis 读取数据所花费的时间:~3 秒。
- 剩余时间在 CPU 上。
Rider 使用上述代码显示:
- 3 未知 ?
- 3 个 ThreadPool 工作线程
- 1 ThreadPool 等待
- 1 个 ThreadPool IO
- 1 个 ThreadPool 门
- 1 .NET 计时器
- 1 个主线程
- 如何查看其他 26-11=15 个线程?更重要的是,如何减少它们?
答:
其中大多数将是线程池线程,你的朋友也是如此。.NET 历来以桌面/服务器环境为目标,目的是使用可用的计算资源,因此以受约束的执行环境为目标确实需要进行调整。ThreadPool.SetMaxThreads
评论
部分响应。
我无法使用 .ThreadPool.SetMaxThreads
可以配置为使用 ThreadPool 中的线程,而不是专用的线程池(由 10 个线程组成):StackExchange.Redis
var configurationOptions = new ConfigurationOptions
{
SocketManager = SocketManager.ThreadPool
};
因此,我能够减少 8 个线程。
评论
async/await