如何正确使用线程池?

25

如果我的理解关于ThreadPool的工作方式是正确的,那么其目的之一就是限制在给定时间内可以创建的进程中的工作线程数。例如,如果您将MaxThreads设置为5并调用QueueUserWorkItem 30次,则会向ThreadPool发出30个请求,但只有其中的5个请求将由一个新线程服务,而其他25个请求将被添加到队列中,并在以前的请求完成并且现有线程可用时一个接一个地服务。

然而,在下面的代码中,对Thread.Sleep(-1)的调用保证了DoSomething()方法永远不会返回,这意味着当前线程永远不会对后续请求可用。

但是,我的对ThreadPool工作方式的理解可能是不正确的,因为如果它是正确的,那么下面的代码只应该打印数字0-4,而不是0-29。

请问有人能解释一下ThreadPool的工作原理,并说明下面的代码为什么不能做我认为它应该做的事情吗?

    static void DoSomething(object n)
    {
        Console.WriteLine(n);
        Thread.Sleep(-1);
    }

    static void Main(string[] args)
    {
        ThreadPool.SetMaxThreads(5, 5);
        for (int x = 0; x < 30; x++)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomething), x);
        }
        Console.Read();
    }

如果您正在使用.NET Framework 4.0或4.5,那么有一个轻量级的库可以实现这一功能,即任务并行库(Task Parallel Library)。TPL使得操作变得简单和轻量化。 - Jobert Enamno
https://dev59.com/T2Uq5IYBdhLWcg3waPzr#14515981 - Parimal Raj
如果我运行你的代码,我只能看到0-4。也许这是在.NET 4.5中发生了变化? - svick
4个回答

19

ThreadPool.SetMaxThreads(5, 5)

这意味着当您有超过5个处理器核心时,活动线程的数量为5,并不意味着ThreadPool只能创建5个线程。ThreadPool最大线程数=CPU内核*250。
Thread.Sleep之后,线程处于非活动状态,因此不会影响其他线程的执行。

如果我用一个无限循环来替换Sleep(),那么会有不同的行为。虽然仍然不是我期望的结果,但已经不同了... - John Smith
这个最大线程数的公式,即MaxThreads=(CPU*250),是在Thread或ThreadPool类中定义的吗? - SHEKHAR SHETE

4

但是,我的线程池工作方式的理解可能不正确,因为如果它正确的话,下面的代码将只打印数字0-4,而不是0-29。

是的,您的假设非常正确。

由于您在ThreadPool中排队了30个作业,并且作业将无限时间睡眠,它们永远不会完成,ThreadPool类将等待一定的间隔来创建新线程,但不会超过最大线程数。

注意

Console.Read()使您的后台线程保持活动状态。

文章

来自MSDN

许多应用程序创建线程,这些线程在睡眠状态下等待事件发生。其他线程可能只会进入睡眠状态,以便定期唤醒以轮询更改或更新状态信息。线程池通过提供由系统管理的一组工作线程,使您的应用程序更有效地使用线程。一个线程监视排队到线程池的几个等待操作的状态。当等待操作完成时,从线程池中选择一个工作线程来执行相应的回调函数。
当所有线程池线程都被分配给任务后,线程池不会立即开始创建新的空闲线程。为了避免为线程不必要地分配堆栈空间,它会定期创建新的空闲线程。目前的间隔时间是半秒钟,尽管在未来的 .NET Framework 版本中可能会更改。
线程池中的线程是后台线程,即它们的IsBackground属性为true。这意味着,在所有前台线程退出后,线程池线程不会使应用程序继续运行。

2

0
通常,线程池会创建与CPU核心数量相等的线程数。没有必要创建更多的线程,因为每个核心一次只能处理一个线程。但是,当排队到线程池的任务执行时间超过0.5秒时,线程池会创建一个额外的线程来处理队列中剩余的任务。因此,如果您将许多重型任务排队到线程池中,它将创建许多额外的线程来模拟多任务并执行所有任务“并行”。但总执行时间将与没有额外线程时相同,而且甚至会更少,因为创建线程是相当繁重的操作。这就是为什么建议使用线程池处理小任务,以避免创建实际上没有带来任何优势的额外线程。
您可以在Albahari的文章中了解有关线程池的更多信息。实际上,他在那里有一系列关于线程的好文章。

我对这两个句子感到困惑:'ThreadPool creates a number of threads equal to number of CPU cores' 和 'ThreadPool maximum number of threads = CPU Core * 250.'(如上所述的接受答案)哪一个是正确的? - SHEKHAR SHETE
两者都可以。线程数等于CPU核心数,通常如果遵循指南,即使用ThreadPool进行快速计算操作,则足够了。但是,如果您成功加载了所有线程,然后排队另一个任务,它将等待约0.5秒钟以释放任何现有线程,如果没有线程被释放,则会创建一个额外的线程来处理您的任务。然后,在完成所有任务并释放线程时,它将终止多余的线程。 - Sergii Vashchyshchuk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接