当前位置: 首页 > 面试题库 >

处理器核心数量与线程池大小的关系

慕容康安
2023-03-14
问题内容

许多次我听说最好将线程池中的线程数保持在该系统中的内核数以下。具有比核心数多两倍或更多的线程不仅浪费,而且还可能导致性能下降。

那些是真的吗?如果不是,那么揭露这些主张的基本原则是什么(特别是与Java有关)?


问题答案:

许多次我听说最好将线程池中的线程数保持在该系统中的内核数以下。具有比核心数多两倍或更多的线程不仅浪费,而且还可能导致性能下降。

这些主张 作为一般性陈述 是不正确的。就是说,有时它们是正确的(或者说是真实的),而有时它们显然是错误的。

有几件事是正确的:

  1. 更多线程意味着更多内存使用。每个线程都需要一个线程堆栈。对于最新的HotSpot JVM, 最小 线程堆栈大小为64Kb,默认值可能高达1Mb。那可能很重要。此外,任何活动线程都可能拥有或共享堆中的对象,无论该线程当前是否可运行。因此,有理由期望更多的线程意味着更大的内存工作集。

  2. JVM实际运行的线程数不能超过执行硬件上的核心(或超线程核心等)。没有引擎,汽车就不会运转;没有核心,线程就不会运转。

除此之外,事情变得不那么明确了。“问题”是活动线程可能处于各种“状态”。例如:

  • 活动线程可以正在运行;即积极执行指令。
  • 活动线程可以运行;即等待一个核心,以便它可以运行。
  • 一个活动线程可以通过同步来实现;即等待来自另一个线程的信号,或等待释放锁。
  • 实时线程可能正在等待外部事件。例如,等待某些外部服务器/服务响应html" target="_blank">请求。

“每核一个线程”启发式方法假定线程正在运行或可运行(根据上述内容)。但是对于许多多线程应用程序,启发式方法是错误的……因为它没有考虑其他状态下的线程。

现在,“太多”线程显然 可以 导致明显的性能下降,这很简单, 只需
使用太多内存即可。(想象一下,您有4Gb的物理内存,并且使用1Mb的堆栈创建了8,000个线程。这就是解决虚拟内存问题的方法。)

但是其他事情呢?线程过多 会导致 过多的上下文切换吗?

我不这么认为。如果你有大量的线程,和你的应用程序使用的线程可能会导致过多的上下文切换,这
不好的性能。但是,我认为上下文切换的根本原因不是实际的线程数。性能问题的根源很可能是该应用程序是:

  • 以特别浪费的方式进行同步;例如使用Object.notifyAll()when Object.notify()会更好,或者
  • 在竞争激烈的数据结构上进行同步,或者
  • 相对于每个线程正在执行的有用工作量,执行的同步过多,或者
  • 尝试并行执行过多的I / O。

(在最后一种情况下,瓶颈可能是I / O系统,而不是上下文切换…,除非I / O是IPC,并且服务/程序在同一台计算机上。)

另一点是,在没有上述混杂因素的情况下,拥有更多线程不会增加上下文切换。如果您的应用程序有N个可运行的线程与M个处理器竞争,并且这些线程是完全无计算和争用的,则OS的线程调度程序将尝试在它们之间进行时间分段。但是时间片的长度很可能以十分之一秒(或更长时间)为单位进行度量,因此与CPU绑定线程在其分片期间实际执行的工作相比,上下文切换开销可以忽略不计。如果我们假设一个时间片的长度是恒定的,那么上下文切换的开销也将是恒定的。添加更多可运行线程(增加N)不会显着改变工作与开销的比率。

总而言之,“线程过多”对性能有害。但是,对于多少是“太多”,没有可靠的通用“经验法则”。并且(幸运的是)在“太多”的性能问题变得严重之前,您通常还有很大的余地。



 类似资料:
  • 当我们用来谈论核心池大小和最大池大小之间的区别到底是什么? 可以借助示例来解释吗?

  • 自定义线程池的建议大小是number_of_cores+1(请参见此处和此处)。假设有一个Spring应用程序在一个系统上有两个核心,配置如下所示 在这种情况下,将有一个ExecutorService在几个请求之间共享。因此,如果有10个请求到达服务器,那么在ExecutorService中只能同时执行其中的3个请求。这可能会产生瓶颈,并且随着请求数量的增加,结果会变得更糟(请记住:默认情况下,t

  • 我有一个图像路径列表,我想在进程或线程之间划分,以便每个进程处理列表的某些部分。处理包括从磁盘加载图像,进行一些计算并返回结果。我正在使用Python 2.7 下面是我如何创建辅助进程 我所面临的问题是,当我在initializer函数中记录初始化时间时,我知道worker不是并行初始化的,而是每个worker都以5秒的间隔初始化,下面是供参考的日志 我尝试过使用将同时启动辅助线程 我知道Wind

  • null 其他地方配置了工人数,当前;是而是。我在一台拥有两个核心和8GB内存的Macbook Pro 10上测试这一点;生产服务器要大得多。我正在与之交谈的数据库背后是一个速度极慢的VPN。我使用Oracle的Javase1.8JVM运行所有这些。本地服务器是Tomcat7。Oracle JDBC驱动程序是10.2版(我也许能够说服那些使用较新版本的能力)。所有方法要么返回要么返回并且应该是非阻

  • 主要内容:1 execute核心提交方法,2 addWorker尝试添加新线程,2.1 addWorkerFailed添加Worker失败处理,3 Worker线程包装类,3.1 runWorker执行工作基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 1 execute核心提交方法 public void execute(Runnable command) 传递一个Runnable任务对象,然后由线程池对它进行异步执行。没有办法检查Runnabl

  • 我提出了一个关于Spark的非常愚蠢的问题,因为我想澄清我的困惑。我对Spark非常陌生,仍在努力理解它在内部是如何工作的。 比方说,如果我有一个输入文件列表(假设1000),我想在某个地方处理或写入,并且我想使用coalesce将我的分区数减少到100。 现在我用12个执行器运行这个作业,每个执行器有5个内核,这意味着它运行时有60个任务。这是否意味着,每个任务将在一个单独的分区上独立工作? 回