如何根据cpu核数来设置线程池线程数 您所在的位置:网站首页 服务器线程数是怎么计算的 如何根据cpu核数来设置线程池线程数

如何根据cpu核数来设置线程池线程数

2024-06-24 00:16| 来源: 网络整理| 查看: 265

线程概念

进程和线程

进程就是起的一个程序一个jar包,线程就是这个进程中具体去完成某个计算的一批指令集合。例如:老板发起一个任务让大家搬砖,这就是一个进程,然后大家都一起搬砖,你就需要把砖在指定地方拿起,运输,放到指定地方,你单个人就是一个线程,多少个人就是多少个线程。

cpu的线程

线程数:是同一时刻设备能并行执行的程序个数。

我们以Intel的Core i5-8250U为例来举例,它是四核八线程的CPU ,一个CPU集成了4个核心,一般来说一个核心对应一个线程,Intel通过超线程技术来实现一个核心对应2个线程,所以它是四核8线程. 这里说的线程是CPU物理级别的,不是java程序里的逻辑线程.

程序的线程

程序的线程和cpu的线程(或者说核数)没有直接关系,一个线程在执行一系列指令,会申请cpu,内存资源,但不会一直占着cpu不放,只有需要使用cpu计算的时候,才会占用cpu,当io或线程sleep时,会把cpu释放,其他的线程依然可以使用释放的cpu。

这也是为什么一个8核的cpu,计算量大的线程 8个线程就能把cpu占到100%,而IO量特别大的线程,起50个线程都不会把这个8核的cpu占满。

项目当中有需要使用线程池来完成某些业务需求,但明显,对于一个需要7*24小时无间断运行的系统,你必须把CPU的使用率保持在70%左右,100%使用很容易造成系统hang住,再加上线程池设置的不合理,导致系统资源持续被占用无法释放,灾难就爆发了。

关于如何计算并发线程数,一般分两派,来自两本书,且都是好书,到底哪个是对的?问题追踪后,整理如下:

第一派:《Java Concurrency in Practice》即《java并发编程实践》,如下图:

如上图,在《Java Concurrency in Practice》一书中,给出了估算线程池大小的公式:

Nthreads=Ncpu*Ucpu*(1+w/c),其中

Ncpu=CPU核心数

Ucpu=cpu使用率,0~1

W/C=等待时间与计算时间的比率

比如你的线程任务需要大量I/O操作,依赖了磁盘、存储性能;如果你依赖网络通讯,那么更考验服务端的处理能力以及你的网络质量;这些情况可以总结是“任务执行的平均时间”、“任务最大的超时时间”、“网络延迟时间”等,或者叫“计算时间”。

假如我计算了以上所有的时间平均是200ms,可以理解成单线程模式tps可以达到5,如果我有大小为100的线程池,那么tps理论上可以达到500;但是要考虑实际的系统资源,比如很常见的中低档服务器配置“4路12核”,其逻辑个数可以达到96,但是我们不可以把线程池大小设置成96,我们一台服务器不可能只运行一个服务,而且我们要考虑其他比如“GC线程”之类的消耗。

java代码获取逻辑个数:

int processNum = Runtime.getRuntime().availableProcessors();//获取Processors System.out.println("逻辑个数:" + processNum);

ThreadPoolsSize = CPU_n * CPU_t * ( 1 + \frac{T_w}{T_c})

线程池最优大小=CPU逻辑数*CPU期望使用率*任务比例(w等待时间、c计算时间)。

96 * 70% * (1+(0/200)) = 96 * 70% * 1 = 67.2 ≈ 67

 

第二派:《Programming Concurrency on the JVM Mastering》即《Java 虚拟机并发编程》

线程数=Ncpu/(1-阻塞系数)

二、分析

对于派系一,假设cpu100%运转,即撇开CPU使用率这个因素,线程数=Ncpu*(1+w/c)。

现在假设将派系二的公式等于派系一公式,即Ncpu/(1-阻塞系数)=Ncpu*(1+w/c),===》阻塞系数=w/(w+c),即阻塞系数=阻塞时间/(阻塞时间+计算时间),这个结论在派系二后续中得到应征,如下图:

由此可见,派系一和派系二其实是一个公式......这样我就放心了......

三、实际应用

那么实际使用中并发线程数如何设置呢?分析如下(我们以派系一公式为例):

Nthreads=Ncpu*(1+w/c)

IO密集型:一般情况下,如果存在IO,那么肯定w/c>1(阻塞耗时一般都是计算耗时的很多倍),但是需要考虑系统内存有限(每开启一个线程都需要内存空间),这里需要上服务器测试具体多少个线程数适合(CPU占比、线程数、总耗时、内存消耗)。如果不想去测试,保守点取1即,Nthreads=Ncpu*(1+1)=2Ncpu。这样设置一般都OK。

计算密集型:假设没有等待w=0,则W/C=0. Nthreads=Ncpu。

至此结论就是:

IO密集型=2Ncpu(可以测试后自己控制大小,2Ncpu一般没问题)(常出现于线程中:数据库数据交互、文件上传下载、网络数据传输等等)

计算密集型=Ncpu(常出现于线程中:复杂算法)

java中:Ncpu=Runtime.getRuntime().availableProcessors()

=========================此处可略过=============================================

当然派系一种《Java Concurrency in Practice》还有一种说法,

即对于计算密集型的任务,在拥有N个处理器的系统上,当线程池的大小为N+1时,通常能实现最优的效率。(即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保CPU的时钟周期不会被浪费。)

即,计算密集型=Ncpu+1,但是这种做法导致的多一个cpu上下文切换是否值得,这里不考虑。读者可自己考量。

2.如何估算线程池大小

2.1 受限于物理机CPU核数

查看linux 逻辑cpu核数

cat /proc/cpuinfo| grep "processor"| wc -l

2.2 受限于程序资源消耗

线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

2.3 一般的估算方式

一般说来,大家认为线程池的大小经验值应该这样设置:(其中N为CPU的个数)

如果是CPU密集型应用,则线程池大小设置为N+1如果是IO密集型应用,则线程池大小设置为2N+1

如果一台服务器上只部署这一个应用并且只有这一个线程池,那么这种估算或许合理,具体还需自行测试验证。 但是,IO优化中,这样的估算公式可能更适合: 最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目 因为很显然,线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

2.4 根据程序调试

越是性能要求高的程序,越是要慢慢调整参数来,慢慢测试,得出测试数据,这才是最准确的,前提是,前面的思想你得理解。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有