c# – 当ThreadPool中的活动线程数大于ThreadPool.GetMinThreads()时启动任务

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

“It’s not the MAX worker threads value you need to look at – it’s the
MIN value you get via ThreadPool.GetMinThreads(). The max value is the
absolute maximum threads that can be active. The min value is the
number to always keep active. If you try to start a thread when the
number of active threads is less than max (and greater than min)
you’ll see a 2 second delay.”

所以,我准备了一个示例代码来测试它:

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.
最小值是始终保持活动的数字.如果在活动线程数小于max(且大于min)时尝试启动线程,则会看到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,需要分配另一个线程,所以应该有一个延迟.

编写此示例应用程序后,我不确定上一个问题中的方案.

解决方法:

这里有几个问题.

>首先,你正在使用你可能不应该使用的Task.Factory.StartNew,在大多数情况下你可能应该使用更现代的Task.Run有大量的问题和博客写在这上面.
>其次,您引用的问题引用的文档比当前框架早.文档已更改.它曾用于规定创建线程的毫秒延迟.
>第三,任务不是线程.

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

文档目前说的是什么.

ThreadPool Class

The thread pool provides new worker threads or I/O completion threads
on demand until it reaches the minimum for each category. When a
minimum is reached, the thread pool can create additional threads in
that category or wait until some tasks complete.

事实是,如果你依赖于任务调度程序的典型行为来以一定的速度分割线程,那么你肯定会做错事.这是一个实现细节,可能会随版本而变化.最多可以提高最小线程数,但任务调度程序的工作是在很大程度上从这个详细程度中抽象出来.它旨在为您做最好的事情.

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

上一篇:如何懒惰地创建供Java线程池使用的任务


下一篇:Java线程池同步