【发布时间】:2018-03-18 04:17:54
【问题描述】:
我们的应用程序创建了许多在 CLR 线程池上运行的任务。 任务数量可能从 10 到 10 千不等。
当任务数量迅速增加时,许多任务无法在给定时间内完成。
这是因为线程池每秒只添加一个新线程(尽管可能有数百个等待任务)。
下面的测试演示了这个问题:
[Test]
public void TestTasks()
{
ThreadPool.SetMaxThreads(10000, 10000);
ThreadPool.SetMinThreads(1, 1);
var source = new CancellationTokenSource();
for (int i=0; i<1000; i++)
{
int ci = i;
Task.Run(() => {
Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + $" {ci} - START");
Thread.Sleep(TimeSpan.FromSeconds(5)); // long operation simulatation
Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + $" {ci} - FINISH");
}, source.Token);
}
Thread.Sleep(TimeSpan.FromSeconds(10));
source.Cancel();
}
典型的输出是
01:38:37.171 0 - START
01:38:37.171 1 - START
01:38:37.172 2 - START
01:38:37.172 3 - START
01:38:38.029 4 - START
01:38:39.029 5 - START
01:38:40.030 6 - START
01:38:41.031 7 - START
01:38:42.029 8 - START
01:38:42.172 0 - FINISH
01:38:42.172 9 - START
01:38:42.173 3 - FINISH
01:38:42.173 1 - FINISH
01:38:42.173 2 - FINISH
01:38:42.173 10 - START
01:38:42.173 11 - START
01:38:42.173 12 - START
01:38:43.029 13 - START
01:38:43.030 4 - FINISH
01:38:43.030 14 - START
01:38:44.030 5 - FINISH
01:38:44.030 15 - START
01:38:44.030 16 - START
01:38:45.029 17 - START
01:38:45.031 6 - FINISH
有什么办法可以提高线程池的增长速度?
更新 1。 上面的示例显示了主要问题:任务是由使用 async/await 样式代码的新现代库创建的。但是大多数任务都是带有线程阻塞操作的旧代码。该应用程序有效。在恒定的低负载或恒定的高负载下,它表现出良好的性能。当任务数量快速增加时,性能问题就开始了:虽然有数千个等待任务,但 CPU 利用率仍然很低。
应对所述情况最简单的方法是什么?
【问题讨论】:
-
不,固定为半秒。这只是针对需要太长时间才能完成工作的 TP 线程的一种对策,而不是要创建线程爆炸。也设置“太长”的小节,半秒。在现代处理器上这是一个简单的约 40 亿个 cpu 指令,除非您计算 pi 的值,否则您只能通过过多的 I/O 延迟到达那里。通用的解决方案是异步代码,不需要 TP 线程来等待 I/O 操作完成。
标签: .net task clr threadpool