线程池与线程产生

时间:2010-01-12 15:15:17

标签: multithreading

有人可以列出Thread Spawning与Thread Pooling之间的一些比较点,哪一个更好?请将.NET框架视为支持两者的参考实现。

12 个答案:

答案 0 :(得分:25)

线程池线程比常规线程便宜得多,它们汇集了线程所需的系统资源。但它们有许多限制可能使它们不合适:

  • 您不能中止线程池线程
  • 没有简单的方法来检测线程池是否已完成,没有Thread.Join()
  • 没有简单的方法来编组来自线程池线程的异常
  • 您无法在消息框之外的线程池线程上显示任何类型的UI
  • 线程池线程的运行时间不应超过几秒
  • 线程池线程不应该长时间阻塞

后两个约束是线程池调度程序的副作用,它尝试将活动线程数限制为CPU可用的核心数。如果您安排许多经常阻塞的长时间运行的线程,这可能会导致长时间的延迟。

许多其他线程池实现具有类似的约束,给予或接受。

答案 1 :(得分:21)

“pool”包含可供使用的可用“线程”列表,而“spawning”是指实际创建新线程。

“线程池”的用处在于“较低的使用时间”:避免了创建时间开销。

就“哪一个更好”而言:取决于。如果创建时间开销是一个问题,请使用Thread-pooling。在需要执行大量“短期任务”的环境中,这是一个常见问题。


正如其他人所指出的,Thread-Pooling存在“管理开销”:如果正确实现,这是最小的。例如。限制池中的线程数是微不足道的。

答案 2 :(得分:10)

对于“更好”的某些定义,您通常希望使用线程池。在不知道您的用例是什么的情况下,请考虑使用线程池,您可以在启动时创建固定数量的线程,也可以按需创建(但线程数不能超过池的大小)。如果一个任务被提交并且没有可用的线程,那么它将被放入队列,直到有一个线程可以自由处理它。

如果您在响应请求或某种其他类型的触发器时产生线程,则存在耗尽所有资源的风险,因为没有任何内容可以限制创建的线程数。

线程池的另一个好处是重用 - 相同的线程被反复使用来处理不同的任务,而不是每次都必须创建一个新的线程。

正如其他人所指出的,如果你有少量任务会运行很长时间,这会否定通过避免频繁创建线程所带来的好处(因为你无论如何都不需要创建大量的线程)

答案 3 :(得分:5)

一切都取决于您的情况。创建新线程是资源密集型和昂贵的操作。大多数非常短的异步操作(最多不到几秒)都可以使用线程池。

对于要在后台运行的较长时间运行的操作,通常会创建(生成)自己的线程。 (Ab)使用平台/运行时内置线程池进行长时间运行可能会导致令人讨厌的死锁形式等。

答案 4 :(得分:5)

我的感觉是你应该只需要根据需要创建一个线程......如果性能良好,那么你就完成了。如果在某些时候,您发现在创建线程时需要较低的延迟,通常可以放入线程池而不会破坏任何内容...

答案 5 :(得分:2)

主要区别在于ThreadPool维护了一组已经旋转并可供使用的线程,因为启动新线程在处理器方面可能是昂贵的。

但是请注意,即使是ThreadPool也需要“生成”线程...它通常依赖于工作负载 - 如果还有很多工作要做,一个好的线程池会根据配置启动新线程来处理负载和系统资源。

答案 6 :(得分:1)

这取决于你想在另一个线程上执行什么。

对于简短任务,最好使用一个线程池,对于长期任务,最好生成一个新线程,因为它可能会使线程池饿死其他任务。

答案 7 :(得分:1)

线程池通常被认为更好,因为线程是预先创建的,并根据需要使用。因此,如果您使用大量线程来完成相对较短的任务,那么它可以快得多。这是因为它们被保存以备将来使用,并且不会被销毁并在以后重新创建。

相比之下,如果你只需要2-3个线程并且它们只会被创建一次,那么这将更好。这是因为您没有从缓存现有线程中获益以备将来使用,并且您没有创建可能未使用的额外线程。

答案 8 :(得分:0)

创建/生成线程所需的额外时间很少,因为线程轮询已包含已准备好使用的已创建线程。

答案 9 :(得分:0)

这个answer是一个很好的总结,但为了以防万一,这里是维基百科的链接:

http://en.wikipedia.org/wiki/Thread_pool_pattern

答案 10 :(得分:0)

对于多线程执行,结合从执行中获取返回值,或者检测线程池已完成的简单方法,可以使用java Callables。

有关详细信息,请参阅https://blogs.oracle.com/CoreJavaTechTips/entry/get_netbeans_6

答案 11 :(得分:0)

假设使用C#和Windows 7及更高版本...

使用new Thread()创建线程时,将创建一个托管线程,当调用Start(一对一关系)时,该线程将由本机OS线程支持。在任何给定时间,只有一个线程在CPU内核上运行很重要。

一种更简单的方法是调用ThreadPool.QueueUserWorkItem(即后台线程),该线程本质上具有相同的作用,除了那些后台线程不会永远绑定到单个本机线程上。 .NET调度程序将在单个本机线程上模拟托管线程之间的多任务。假设有4个核心,您将拥有4个本机线程,每个本机线程运行多个由.NET确定的托管线程。由于托管线程之间的切换发生在.NET VM而不是内核中,因此这提供了更轻量的多任务处理。从用户模式到内核模式的转换有一些开销,.NET调度程序将这种转换减到最少。

需要特别注意的是,精心设计的多线程框架中的纯本地操作系统线程可能会使繁重的多任务处理受益。但是,性能收益并没有那么多。

使用ThreadPool时,只需确保最小工作线程数足够高,否则ThreadPool.QueueUserWorkItem将比新的Thread()慢。在基准测试中,循环512次调用new Thread(),使ThreadPool.QueueUserWorkItem处于默认最小值。但是,在此测试中,首先将最小工作线程数设置为512,这使得new Thread()和ThreadPool.QueueUserWorkItem的性能类似。

设置较高的工作线程数的一个有效方面是,新Task()(或Task.Factory.StartNew)的执行方式也与new Thread()和ThreadPool.QueueUserWorkItem相似。