如何配置线程池大小

引言

        很多人甚至可能都会觉得把线程池配置过大一点比较好!这是有问题的,就拿生活中常见的一例子来说:并不是人多就能把事情做好,增加了沟通交流成本。当一件事情只需要 3 个人做,你硬是拉来了 6 个人,会提升做事效率嘛?可能并不会。 线程数量过多的影响也是和我们分配多少人做事情一样,对于多线程这个场景来说主要是增加了上下文切换成本。


线程上下文切换:

        多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。

        概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。上下文切换通常是计算密集型的。它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。

        类比于实现世界中的人类通过合作做某件事情,可以肯定的一点是线程池大小设置过大或者过小都会有问题,合适的才是最好。如果设置的线程池数量太小的话,如果同一时间有大量任务/请求需要处理,可能会导致大量的请求/任务在任务队列中排队等待执行,甚至会出现任务队列满了之后任务/请求无法处理的情况,或者大量任务堆积在任务队列导致 OOM。这样很明显是有问题的,CPU 根本没有得到充分利用。如果我们设置线程数量太大,大量线程可能会同时在争取 CPU 资源,这样会导致大量的上下文切换,从而增加线程的执行时间,影响了整体执行效率。有一个简单并且适用面比较广的公式。

CPU 密集型任务(N+1):

        CPU 密集型任务消耗的主要是 CPU 资源,可将线程数设置为 N(CPU 核心数)+1。比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。

I/O 密集型任务(2N):

         I/O 密集型任务,系统会用大部分时间处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。如何判断是 CPU 密集任务还是 IO 密集任务?CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行排序。但凡涉及到网络读取,文件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上。

拓展一刻

🌈 拓展一下(参见:issue#1737open in new window):线程数更严谨的计算的方法应该是:最佳线程数 = N(CPU 核心数)∗(1+WT(线程等待时间)/ST(线程计算时间)),其中 WT(线程等待时间)=线程运行总时间 - ST(线程计算时间)。线程等待时间所占比例越高,需要越多线程。线程计算时间所占比例越高,需要越少线程。我们可以通过 JDK 自带的工具 VisualVM 来查看 WT/ST 比例。CPU 密集型任务的 WT/ST 接近或者等于 0,因此, 线程数可以设置为 N(CPU 核心数)∗(1+0)= N,和我们上面说的 N(CPU 核心数)+1 差不多。IO 密集型任务下,几乎全是线程等待时间,从理论上来说,你就可以将线程数设置为 2N(按道理来说,WT/ST 的结果应该比较大,这里选择 2N 的原因应该是为了避免创建过多线程吧)。公式也只是参考,具体还是要根据项目实际线上运行情况来动态调整。

其他参考

        线程池大小的配置取决于应用程序需求、系统资源和性能考量。一般来说,可以根据以下因素来配置线程池的大小:

  1. 系统资源: 确保线程池的大小不会超过系统资源的限制,如可用内存和处理器核心数。

  2. 任务性质: 如果任务是计算密集型的,通常可以选择一个较小的线程池大小,以充分利用处理器核心。如果是IO密集型任务,可以选择一个较大的线程池大小,以充分利用等待IO的时间。

  3. 任务队列: 如果任务队列的长度也很大,可能需要一个较大的线程池来处理这些任务。如果任务队列长度很小,可能只需要一个较小的线程池。

一般来说,可以通过以下步骤来配置线程池大小:

  1. 确定系统资源: 确定系统可用资源,如处理器核心数、可用内存等。

  2. 根据任务性质选择线程池大小: 如果是计算密集型任务,可以选择一个与处理器核心数相近的线程池大小;如果是IO密集型任务,可以选择一个稍大一些的线程池大小,以充分利用等待IO的时间。

  3. 测试和优化: 根据实际情况测试线程池的大小,并根据性能指标进行优化。

更多消息资讯,请访问昂焱数据

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值