CPU的核心数、线程数的关系和区别 同步与堵塞完全是两码事

线程池应该设置多少线程合适,怎么样估算出来。最近接触到一些相关资料,现作如下总结。

最开始接触线程池的时候,没有想到就仅仅是设置一个线程池的大小居然还有这么多的学问,汗颜啊。

首先,需要考虑到线程池所进行的工作的性质:

  • IO密集型
  • CPU密集型

简单的分析来看,如果是CPU密集型的任务,我们应该设置数目较小的线程数,比如CPU数目加1。如果是IO密集型的任务,则应该设置可能多的线程数,由于IO操作不占用CPU,所以,不能让CPU闲下来。当然,如果线程数目太多,那么线程切换所带来的开销又会对系统的响应时间带来影响。

在《linux多线程服务器端编程》中有一个思路,CPU计算和IO的阻抗匹配原则

如果线程池中的线程在执行任务时,密集计算所占的时间比重为P(0<P<=1),而系统一共有C个CPU,为了让CPU跑满而又不过载,线程池的大小经验公式 T = C / P。在此,T只是一个参考,考虑到P的估计并不是很准确,T的最佳估值可以上下浮动50%。

这个经验公式的原理很简单,T个线程,每个线程占用P的CPU时间,如果刚好占满C个CPU,那么必有 T * P = C。

下面验证一下边界条件的正确性:

假设C = 8,P = 1.0,线程池的任务完全是密集计算,那么T = 8。只要8个活动线程就能让8个CPU饱和,再多也没用了,因为CPU资源已经耗光了。

假设C = 8,P = 0.5,线程池的任务有一半是计算,有一半在等IO上,那么T = 16.考虑操作系统能灵活,合理调度sleeping/writing/running线程,那么大概16个“50%繁忙的线程”能让8个CPU忙个不停。启动更多的线程并不能提高吞吐量,反而因为增加上下文切换的开销而降低性能。

如果P < 0.2,这个公式就不适用了,T可以取一个固定值,比如 5*C。另外公式里的C不一定是CPU总数,可以是“分配给这项任务的CPU数目”,比如在8核机器上分出4个核来做一项任务,那么C=4

文章如何合理设置线程池大小里面提到了一个公式:

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。这个公式进一步转化为:

最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目

可以得出一个结论:
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程

以上公式与之前的CPU和IO密集型任务设置线程数基本吻合。




CPU的核心数、线程数的关系和区别

我们在选购电脑的时候,CPU是一个需要考虑到核心因素,因为它决定了电脑的性能等级。CPU从早期的单核,发展到现在的双核,多核。CPU除了核心数之外,还有线程数之说,下面笔者就来解释一下CPU的核心数与线程数的关系和区别。
  简单地说,CPU的核心数是指物理上,也就是硬件上存在着几个核心。比如,双核就是包括2个相对独立的CPU核心单元组,四核就包含4个相对独立的CPU核心单元组,等等,依次类推。
  线程数是一种逻辑的概念,简单地说,就是模拟出的CPU核心数。比如,可以通过一个CPU核心数模拟出2线程的CPU,也就是说,这个单核心的CPU被模拟成了一个类似双核心CPU的功能。我们从任务管理器的性能标签页中看到的是两个CPU。
  比如Intel 赛扬G460是单核心,双线程的CPU,Intel 酷睿i3 3220是双核心 四线程,Intel 酷睿i7 4770K是四核心 八线程 ,Intel 酷睿i5 4570是四核心 四线程等等。
  对于一个CPU,线程数总是大于或等于核心数的。一个核心最少对应一个线程,但通过超线程技术,一个核心可以对应两个线程,也就是说它可以同时运行两个线程。
  CPU的线程数概念仅仅只针对Intel的CPU才有用,因为它是通过Intel超线程技术来实现的,最早应用在Pentium4上。如果没有超线程技术,一个CPU核心对应一个线程。所以,对于AMD的CPU来说,只有核心数的概念,没有线程数的概念。
  CPU之所以要增加线程数,是源于多任务处理的需要。线程数越多,越有利于同时运行多个程序,因为线程数等同于在某个瞬间CPU能同时并行处理的任务数。
  在Windows中,在cmd命令中输入“wmic”,然后在出现的新窗口中输入“cpu get *”即可查看物理CPU数、CPU核心数、线程数。其中,
  Name:表示物理CPU数
  NumberOfCores:表示CPU核心数
  NumberOfLogicalProcessors:表示CPU线程数

在Java中通过Runtime.getRuntime().availableProcessors();获得OS线程数,

查看电脑支持的核心数和线程数在电脑上也能看,通过查看电脑属性选项即可。


同步与堵塞完全是两码事

有人觉得堵塞就是同步,非堵塞就是异步,其实以前我也是这么想的,其实同步与堵塞这完全是两码事,所以写篇文章来说说为什么是两码事,也顺便说说各种组合的可以达到的效果,帮助大家了解底层的原理.

       首先需要了解这些概念,OS里面有内核态和用户态两种,程序进行IO操作的时候一般是两步,第一步是IO初始化也就是准备好IO操作,第二步就是真正的IO操作.其中第一步决定同步还是异步,第二步决定堵塞还是非堵塞的,为什么这么说呢,我们以Linux为例来看看这个关键字的组合

1.    同步堵塞IO

2.    同步非堵塞IO

3.    异步堵塞IO

4.    异步非堵塞IO

同步堵塞IO:就是最最普通的write/read操作,以读操作为例, 程序调用内核接口,然后等待系统返回,内核做读写初始化操作(寻址,读取原始数据到内核等),接着将数据读出到内核态,然后把数据从内核态拷贝到用户态下并返回结果给程序,然后程序才知道完成整个读操作,在内核初始化到将数据从存储介质取出到内核态内存中的这段时间程序就是一直等待,期间CPU的状态是wait的,夸张一点,如果内核调用一次读的操作是1秒的话,那么这一秒内程序是将CPU占着不做任何事情的,是不是感觉好浪费.

同步非堵塞IO:这里再内核调用里面会有一个设置参数叫O_NONBLOCK,使用这个的时候就会变成了非堵塞的调用,(其实从这个参数差不多可以猜测出堵塞和非堵塞是针对内核调用来说的,因为这个参数直接作用的就是内核调用),这里会有一个状态通知的概念,程序不会立即完成IO操作,还是以读操作为例,程序首先向内核发出一个读操作的调用,这个时候系统会在很短的时候返回一个状态,表示读操作是否初始化完成,大多数时候是返回不能读初始化(因为有资源竞争),接着程序会继续像死循环一个的发起这个请求直到内核返回读操作已经准备好(记住死循环的时候程序实际上被读这个操作的时候实际不能做别的事情,所以是同步的),这个时候程序在才能将数据从内核态拷贝到用户态后并且返回,这个读过程就算完成了.虽然内核态是是非堵塞的,但是程序这个线程里面在完成读操作之前也没有能做别的事情,所以这里还是同步的.相对同步堵塞IO来说,这种方式可以让程序不用占着CPU时间也不给别人用的情况.,另外由于操作是循环调用查看状态的,所以再内核准备好数据到程序下一次查看状态之间存在延时,这样会导致系统的吞吐量下降.

异步堵塞IO:这里就是我们java里面用到的Select或者Clib里面的poll来实现的,这里面实际也用到上面提到的非阻塞的参数,只是采用Select这种阻塞的方式来调用非阻塞的内核调用.虽然读和写的操作并没有被堵塞住,而是由Select中获取了I/O从操作符再进行下一步操作,这里的数据真正的操作还是被堵塞住了,但是这个的好处就是可以同时再一个线程里面对多个IO操作进行处理,这种顺序处理的效率确实不高,再有的程序里面会启动多个线程来做Select的操作提高性能.这个一个的好处就是再同一个线程内可以有多个IO操作同时进行,他不能提高单个IO的吞吐量,但是可以提高程序的IO并发能力,从而提高整体的IO吞吐量.

异步非堵塞IO:这个厉害了,这就是我们提得比较热的一个概念,叫AIO,他采用的是回调的方式实现异步操作,然后也使用非堵塞的参数,这样一来程序不需要死循环来监听IO操作符,内核准备好了就会通知程序做对应的事情,而程序再读写等待的时候就可以把剩下的CPU时间用在做别的业务处理.这个的好处就太多了,首先他吧CPU时间让出来了,可以做别的事情,其次是回调方式的,只要数据准备好就可以回调让程序把数据从内核拷贝走.坏的地方么我只能说回调对于程序员的编码要求确实有点高的.

所以总结一下这四个方式:同步堵塞IO在IO操作开始的时候就堵塞程序,这个时候还不能重复的进行别的IO操作,同步非堵塞IO解决了内核可以同时处理来自程序的多个IO请求.而异步堵塞主要是解决在一个线程里面处理多个IO的操作,他并没有对IO进行堵塞,但是对事件进行了堵塞(Select实际上实在选择事件),最后的终极大咖异步非堵塞IO,实现了内核非堵塞和回调通知的方法,让IO的操作更高效.

   那是不是异步非堵塞是最好的呢,这个不会一定,我们在做大数据的时候会知道程序有CPU密集型和IO密集型两种区别,至于那种类型使用哪种方式的IO模式这个就需要看攻城狮自己的判断了
补充:
忘了说了,LInux在2.6内核里面才讲AIO作为标准标准特性.


        </div>
            </div>

线程池应该设置多少线程合适,怎么样估算出来。最近接触到一些相关资料,现作如下总结。

最开始接触线程池的时候,没有想到就仅仅是设置一个线程池的大小居然还有这么多的学问,汗颜啊。

首先,需要考虑到线程池所进行的工作的性质:

  • IO密集型
  • CPU密集型

简单的分析来看,如果是CPU密集型的任务,我们应该设置数目较小的线程数,比如CPU数目加1。如果是IO密集型的任务,则应该设置可能多的线程数,由于IO操作不占用CPU,所以,不能让CPU闲下来。当然,如果线程数目太多,那么线程切换所带来的开销又会对系统的响应时间带来影响。

在《linux多线程服务器端编程》中有一个思路,CPU计算和IO的阻抗匹配原则

如果线程池中的线程在执行任务时,密集计算所占的时间比重为P(0<P<=1),而系统一共有C个CPU,为了让CPU跑满而又不过载,线程池的大小经验公式 T = C / P。在此,T只是一个参考,考虑到P的估计并不是很准确,T的最佳估值可以上下浮动50%。

这个经验公式的原理很简单,T个线程,每个线程占用P的CPU时间,如果刚好占满C个CPU,那么必有 T * P = C。

下面验证一下边界条件的正确性:

假设C = 8,P = 1.0,线程池的任务完全是密集计算,那么T = 8。只要8个活动线程就能让8个CPU饱和,再多也没用了,因为CPU资源已经耗光了。

假设C = 8,P = 0.5,线程池的任务有一半是计算,有一半在等IO上,那么T = 16.考虑操作系统能灵活,合理调度sleeping/writing/running线程,那么大概16个“50%繁忙的线程”能让8个CPU忙个不停。启动更多的线程并不能提高吞吐量,反而因为增加上下文切换的开销而降低性能。

如果P < 0.2,这个公式就不适用了,T可以取一个固定值,比如 5*C。另外公式里的C不一定是CPU总数,可以是“分配给这项任务的CPU数目”,比如在8核机器上分出4个核来做一项任务,那么C=4

文章如何合理设置线程池大小里面提到了一个公式:

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。这个公式进一步转化为:

最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目

可以得出一个结论:
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程

以上公式与之前的CPU和IO密集型任务设置线程数基本吻合。




CPU的核心数、线程数的关系和区别

我们在选购电脑的时候,CPU是一个需要考虑到核心因素,因为它决定了电脑的性能等级。CPU从早期的单核,发展到现在的双核,多核。CPU除了核心数之外,还有线程数之说,下面笔者就来解释一下CPU的核心数与线程数的关系和区别。
  简单地说,CPU的核心数是指物理上,也就是硬件上存在着几个核心。比如,双核就是包括2个相对独立的CPU核心单元组,四核就包含4个相对独立的CPU核心单元组,等等,依次类推。
  线程数是一种逻辑的概念,简单地说,就是模拟出的CPU核心数。比如,可以通过一个CPU核心数模拟出2线程的CPU,也就是说,这个单核心的CPU被模拟成了一个类似双核心CPU的功能。我们从任务管理器的性能标签页中看到的是两个CPU。
  比如Intel 赛扬G460是单核心,双线程的CPU,Intel 酷睿i3 3220是双核心 四线程,Intel 酷睿i7 4770K是四核心 八线程 ,Intel 酷睿i5 4570是四核心 四线程等等。
  对于一个CPU,线程数总是大于或等于核心数的。一个核心最少对应一个线程,但通过超线程技术,一个核心可以对应两个线程,也就是说它可以同时运行两个线程。
  CPU的线程数概念仅仅只针对Intel的CPU才有用,因为它是通过Intel超线程技术来实现的,最早应用在Pentium4上。如果没有超线程技术,一个CPU核心对应一个线程。所以,对于AMD的CPU来说,只有核心数的概念,没有线程数的概念。
  CPU之所以要增加线程数,是源于多任务处理的需要。线程数越多,越有利于同时运行多个程序,因为线程数等同于在某个瞬间CPU能同时并行处理的任务数。
  在Windows中,在cmd命令中输入“wmic”,然后在出现的新窗口中输入“cpu get *”即可查看物理CPU数、CPU核心数、线程数。其中,
  Name:表示物理CPU数
  NumberOfCores:表示CPU核心数
  NumberOfLogicalProcessors:表示CPU线程数

在Java中通过Runtime.getRuntime().availableProcessors();获得OS线程数,

查看电脑支持的核心数和线程数在电脑上也能看,通过查看电脑属性选项即可。


同步与堵塞完全是两码事

有人觉得堵塞就是同步,非堵塞就是异步,其实以前我也是这么想的,其实同步与堵塞这完全是两码事,所以写篇文章来说说为什么是两码事,也顺便说说各种组合的可以达到的效果,帮助大家了解底层的原理.

       首先需要了解这些概念,OS里面有内核态和用户态两种,程序进行IO操作的时候一般是两步,第一步是IO初始化也就是准备好IO操作,第二步就是真正的IO操作.其中第一步决定同步还是异步,第二步决定堵塞还是非堵塞的,为什么这么说呢,我们以Linux为例来看看这个关键字的组合

1.    同步堵塞IO

2.    同步非堵塞IO

3.    异步堵塞IO

4.    异步非堵塞IO

同步堵塞IO:就是最最普通的write/read操作,以读操作为例, 程序调用内核接口,然后等待系统返回,内核做读写初始化操作(寻址,读取原始数据到内核等),接着将数据读出到内核态,然后把数据从内核态拷贝到用户态下并返回结果给程序,然后程序才知道完成整个读操作,在内核初始化到将数据从存储介质取出到内核态内存中的这段时间程序就是一直等待,期间CPU的状态是wait的,夸张一点,如果内核调用一次读的操作是1秒的话,那么这一秒内程序是将CPU占着不做任何事情的,是不是感觉好浪费.

同步非堵塞IO:这里再内核调用里面会有一个设置参数叫O_NONBLOCK,使用这个的时候就会变成了非堵塞的调用,(其实从这个参数差不多可以猜测出堵塞和非堵塞是针对内核调用来说的,因为这个参数直接作用的就是内核调用),这里会有一个状态通知的概念,程序不会立即完成IO操作,还是以读操作为例,程序首先向内核发出一个读操作的调用,这个时候系统会在很短的时候返回一个状态,表示读操作是否初始化完成,大多数时候是返回不能读初始化(因为有资源竞争),接着程序会继续像死循环一个的发起这个请求直到内核返回读操作已经准备好(记住死循环的时候程序实际上被读这个操作的时候实际不能做别的事情,所以是同步的),这个时候程序在才能将数据从内核态拷贝到用户态后并且返回,这个读过程就算完成了.虽然内核态是是非堵塞的,但是程序这个线程里面在完成读操作之前也没有能做别的事情,所以这里还是同步的.相对同步堵塞IO来说,这种方式可以让程序不用占着CPU时间也不给别人用的情况.,另外由于操作是循环调用查看状态的,所以再内核准备好数据到程序下一次查看状态之间存在延时,这样会导致系统的吞吐量下降.

异步堵塞IO:这里就是我们java里面用到的Select或者Clib里面的poll来实现的,这里面实际也用到上面提到的非阻塞的参数,只是采用Select这种阻塞的方式来调用非阻塞的内核调用.虽然读和写的操作并没有被堵塞住,而是由Select中获取了I/O从操作符再进行下一步操作,这里的数据真正的操作还是被堵塞住了,但是这个的好处就是可以同时再一个线程里面对多个IO操作进行处理,这种顺序处理的效率确实不高,再有的程序里面会启动多个线程来做Select的操作提高性能.这个一个的好处就是再同一个线程内可以有多个IO操作同时进行,他不能提高单个IO的吞吐量,但是可以提高程序的IO并发能力,从而提高整体的IO吞吐量.

异步非堵塞IO:这个厉害了,这就是我们提得比较热的一个概念,叫AIO,他采用的是回调的方式实现异步操作,然后也使用非堵塞的参数,这样一来程序不需要死循环来监听IO操作符,内核准备好了就会通知程序做对应的事情,而程序再读写等待的时候就可以把剩下的CPU时间用在做别的业务处理.这个的好处就太多了,首先他吧CPU时间让出来了,可以做别的事情,其次是回调方式的,只要数据准备好就可以回调让程序把数据从内核拷贝走.坏的地方么我只能说回调对于程序员的编码要求确实有点高的.

所以总结一下这四个方式:同步堵塞IO在IO操作开始的时候就堵塞程序,这个时候还不能重复的进行别的IO操作,同步非堵塞IO解决了内核可以同时处理来自程序的多个IO请求.而异步堵塞主要是解决在一个线程里面处理多个IO的操作,他并没有对IO进行堵塞,但是对事件进行了堵塞(Select实际上实在选择事件),最后的终极大咖异步非堵塞IO,实现了内核非堵塞和回调通知的方法,让IO的操作更高效.

   那是不是异步非堵塞是最好的呢,这个不会一定,我们在做大数据的时候会知道程序有CPU密集型和IO密集型两种区别,至于那种类型使用哪种方式的IO模式这个就需要看攻城狮自己的判断了
补充:
忘了说了,LInux在2.6内核里面才讲AIO作为标准标准特性.


        </div>
            </div>

猜你喜欢

转载自blog.csdn.net/bsfz_2018/article/details/80105138