为啥通过调用Task.Run和ThreadPool.QueueUserWorkItem排队到ThreadPool时线程数增加了不止一个?

Posted

技术标签:

【中文标题】为啥通过调用Task.Run和ThreadPool.QueueUserWorkItem排队到ThreadPool时线程数增加了不止一个?【英文标题】:Why threads count is increased by more than one when queuing to ThreadPool by calling Task.Run and ThreadPool.QueueUserWorkItem?为什么通过调用Task.Run和ThreadPool.QueueUserWorkItem排队到ThreadPool时线程数增加了不止一个? 【发布时间】:2020-03-11 16:27:03 【问题描述】:

我试图在 Thread 的帮助下计算控制台应用程序中正在运行的线程数:

new Thread(() =>
        
            while (true)
            
                Thread.Sleep(500);
                Console.WriteLine(System.Diagnostics.Process.GetCurrentProcess().Threads.Count);
            
        ).Start();

然后我开始了新的Thread,其中包含非常简单的执行逻辑。毫无疑问,线程数增加了1。对于ThreadPool.QueueUserWorkItem,它增加了 3。

并对Task.Run(() => ...) 做了同样的测试。令人惊讶的是,线程数增加了4 或有时5。不幸的是,我无法理解原因。

这里是完整的测试代码:

static void Main(string[] args)

    new Thread(() =>
    
        while (true)
        
            Thread.Sleep(500);
            Console.WriteLine(System.Diagnostics.Process.GetCurrentProcess().Threads.Count);
        
    ).Start();

    //Task.Run(() =>
    //
    //    Thread.Sleep(5000);
    //); // thread count is 12

    //new TaskFactory().StartNew(() =>
    //
    //    Thread.Sleep(5000);
    //); // thread count is 12

    //ThreadPool.QueueUserWorkItem((onj) =>
    //
    //    Thread.Sleep(5000);
    //); // thread count is 9

    //new Thread(() =>
    //       
    //    Thread.Sleep(5000);
    //).Start(); // thread count is 7

    Console.ReadLine();

我的问题是,为什么运行新任务会使线程数增加 4 或 5?为什么为线程池排队工作项会使线程数增加 3?

【问题讨论】:

@Fredou 为了获得计数,我在问题的第一行与您分享了专门的线程 TaskTask<T> 合作的要点之一是专注于手头的工作,而不是机制 .为什么您如此关心 runtime 在任何特定时间恰好运行了多少线程? @Damien_The_Unbeliever 我正在比较两种不同实现的运行线程数。因为,运行线程越少,上下文切换就越少,依此类推。并且无法找出为什么计数如此增加。然后,我编写了那个简单的代码来找出差异。我相信它在某处有记录,但我没有运气找到它 请注意,线程出现在GetCurrentProcess().Threads 中并不意味着它正在运行。这可能是一个没有做任何事情的线程池。 @canton7 我们在同一条路上。我会稍等一下,然后可能会删除答案。我想这可能是我忽略了一些细节。 【参考方案1】:

线程池使用complicated mechanism 来确定将运行多少线程。

当您通过调用QueueUserItem 或创建Task 将工作排队到线程池时,线程池将使用默认的初始线程数进行初始化,通常为四个。

【讨论】:

您能否分享一下有关线程池中默认初始线程数的文档? @FarhadJabiyev 你可以调用ThreadPool.GetMinThreads 方法来获取这些值,虽然文档说:当需求低时,线程池线程的实际数量可能会低于最小值。在我的 PC 中是 4 个(和 4 个 I/O 完成线程)。 @Nick 感谢您的回答。它克服了我的困惑。也会尝试阅读那篇文章。

以上是关于为啥通过调用Task.Run和ThreadPool.QueueUserWorkItem排队到ThreadPool时线程数增加了不止一个?的主要内容,如果未能解决你的问题,请参考以下文章

为啥不调用 Task<T>.Result 死锁?

为啥不等待 Task.Run() 同步回 UI 线程/原始上下文?

CS0121'Task.Run之间的调用不明确 (Func键 )'和'Task.Run(Func )”

Task.Factory.StartNew 和 Task.Run

task 异步

Task.Delay 使用注意事项