当ThreadPool中的活动线程数大于ThreadPool.GetMinThreads()

时间:2018-12-04 07:40:50

标签: c# multithreading task threadpool

取自我之前的一个问题的答案 (Task.Factory.StartNew starts with a great delay despite having available threads in threadpool):

  

“这不是您需要查看的MAX工作线程值-它是   您通过ThreadPool.GetMinThreads()获得的MIN值。最大值是   可以激活的绝对最大线程数。最小值是   始终保持活动状态的号码。如果您尝试在启动线程时   活动线程数小于最大值(大于最小值)   您会看到2秒的延迟。”

因此,我准备了一个示例代码对其进行测试:

ThreadPool.GetMinThreads()为我的机器返回“ 8”,并且我在本地机器上运行代码。

我的代码如下:

        Task task = null;

        int workerThreads = 0;
        int completionPortThreads = 0;



        ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
        Logger.WriteInfo(LogCode.EMPTY_INFO,  workerThreads.ToString());**returns "8"**

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread1");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread1");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread2");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread2");
            while (true)
            {
                DoSomthing();
            }
        });
    ;
        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread3");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread3");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread4");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread4");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread5");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread5");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread6");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread6");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread7");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread7");
            while (true)
            {
                DoSomthing();
            }
        });


        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread8");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread8");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread10");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread10");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread11");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread11");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread12");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread12");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread13");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread13");
            while (true)
            {
                DoSomthing();
            }
        });


private void DoSomthing()
    {
        int j = 1;
        for (int i = 0; i < 2000; i++)
        {
            j = i * i;
        }
    }

Logger类仅使用log4net.dll。 因此,ThreadPool.GetMinThreads()为我的机器返回8。 最小值是始终保持活动状态的数字。如果您尝试在活动线程数小于最大值(大于最大值)时启动线程,则会看到2秒的延迟。

因此,对于9号线程,依此类推:

Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");
    task = Task.Factory.StartNew(() =>
    {
        Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");
        while (true)
        {
            DoSomthing();
        }
    });

我希望之间有2秒的延迟

Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");

 Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");

因为当活动线程数大于min(大于8)时,我尝试从线程池启动线程。 实际结果是只有几毫秒的延迟 (不到半秒)用于所有线程9至13。

为什么至少不延迟2秒?我的意思是,我的线程池中的所有活动线程都很忙,对于线程9,需要分配另一个线程,因此应该有一个延迟。

编写此示例应用程序后,我不确定上一个问题中的场景。

2 个答案:

答案 0 :(得分:2)

这里有两个问题。

  • 首先是,您使用的是Task.Factory.StartNew,您可能不应该这样做,在大多数情况下,您可能应该使用更现代的Task.Run问题和关于此的博客。

  • 第二,您引用的问题是引用的文档要比当前框架的文档早。文档已更改。它过去规定了创建线程的毫秒级延迟。

  • 第三次,任务不是线程。

我对此的理解是任务调度程序(取决于您使用的任务调度程序),它使用启发式方法来确定是否要在每个类别中为您提供线程,并且没有任意毫秒的延迟。

文档当前所说的是

ThreadPool Class

  

线程池提供新的工作线程或I / O完成线程   直到达到每个类别的最小值为止。当一个   达到最小值后,线程池可以在其中创建其他线程   该类别或等到某些任务完成。

事实是,如果您依靠任务调度程序的典型行为以一定的速度分配线程,那么您肯定在做错事。这是一个实现细节,可能会因版本而异。充其量您最多可以增加线程的最小数量,但是任务调度程序的工作是在很大程度上将您从这种详细程度中抽象出来。它旨在为您做最好的事情。

如果您需要一定数量的线程,请构建自己的任务计划程序,或者创建自己的线程并一起跳过任务计划程序

答案 1 :(得分:1)

好吧,在查阅ThreadPool的MSDN文档时,我没有发现与达到大于最小值的2秒延迟相关的任何信息,并且我认为这不是准确的答案,因为延迟取决于与硬件和操作系统相关的许多因素,请指定以下内容:

  

默认情况下,最小线程数设置为系统上的处理器数。达到最小值后,线程池可以在该类别中创建其他线程,或者等待某些任务完成。从.NET Framework 4开始,线程池创建和销毁线程以优化吞吐量,吞吐量定义为每单位时间完成的任务数。线程太少可能无法充分利用可用资源,而线程过多可能会增加资源争用。

GetMinThreads的文档如下:

  

在进行新请求时,在切换到用于管理线程创建和销毁的算法之前,检索线程池按需创建的最小线程数

文档没有说明有关该 Switching Algorithm 的更多详细信息,所以这2秒钟的延迟是由他的机器上进行测试得出的?

但是,如果您需要更多有关.NET框架Source的信息,则可以深入检查算法,但是由于硬件和操作系统的依赖性,您不会获得静态的延迟数字。因素。