创建多少线程才是合适的

首先,我们要搞懂两个问题:

  1. 为什么要使用多线程?
  2. 多线程的应用场景有哪些?

♣ 为什么要使用多线程?

使用多线程本质上是提升程序性能。那么衡量性能的指标又包括哪些?

  1. 延迟:延迟是指发出请求到收到响应这个过程的时间。延迟越短,意味着程序执行的越快,性能越好。
  2. 吞吐量:吞吐量指的是单位时间内能处理请求的数量。吞吐量越大,意味着程序能处理的请求越多,性能越好。

我们所谓提升性能,主要是降低延迟,提高吞吐量。要想实现这个目的,先要了解多线程的使用场景。

♣ 多线程的应用场景

在并发编程领域,提升性能本质就是提升硬件的利用率,也就是提升 I/O 和 CPU 利用率。操作系统解决硬件利用率问题的对象往往是单一硬件设备,而并发程序往往需要 CPU 和 I/O 设备相互配合工作,所以我们要考虑 CPU 和 I/O 设备综合利用率的问题。

下面我们看看多线程是如何提升 CPU 和 I/O 利用率的。假设程序按照 CPU 计算和 I/O 操作交叉执行的方式运行,而且 CPU 计算和 I/O 操作的耗时是 1:1.

如图,当只有一个线程,执行CPU计算时,I/O设备空闲;执行I/O操作时,CPU空闲。所以两者利用率都是50%。
                  在这里插入图片描述
如果又增加一个线程,如图,线程A执行 CPU 计算时,线程B执行 I/O 操作;线程B执行 CPU 计算时,线程A执行 I/O 操作;这样 CPU 和 I/O 设备的利用率就达到了 100%。
                 在这里插入图片描述
刚刚我们将 CPU 和 I/O 设备的利用率都提升到了100%,单位时间处理的请求数量翻了一倍,也就是说吞吐量提高了1倍。反过来想:如果CPU 和 I/O 设备的利用率都很低,那么可以尝试通过增加线程来提高吞吐量

单核CPU时代,多线程主要是用力啊平衡 CPU 和 I/O 设备的。如果程序只有CPU计算,而没有I/O操作,多线程不会使性能提升反而会下降,原因是多线程增加了线程切换的成本。不过在多核时代,这种纯计算型的场景可以利用多线程提升性能,因为利用多核可以降低响应时间。

举个例子:
计算 1+2+…+100亿的值,如果用4核CPU的程序执行,A线程计算(1,25亿),B线程计算(25亿,50亿),C线程计算(50亿,75亿),D线程计算(75亿,100亿),最后汇总。这样理论上会比一个线程计算[1,100亿]快4倍左右,响应时间能够降低到25%。一个线程,对于4核的CPU,利用率只有25%,而4个线程,则能够将CPU的利用率提高到100%。

                 在这里插入图片描述

♣ 创建多少线程合适?

创建多少线程才是合适的,要看多线程具体的应用场景。一般有两种情况:

  1. I/O密集型
  2. CPU密集型

I/O密集型程序和CPU密集型程序,计算最佳线程数的方法不同。
下面我们分开来说。

♠ CPU密集型

对于CPU密集型计算,多线程本质上是提升CPU的利用率,所以对于一个4核的CPU,每个核一个线程,理论上创建4个线程就可以了,再多创建线程也只会增加线程切换成本。所以,对于CPU密集型的计算场景,理论上“线程数量 = CPU 核数”就是最合适的。不过实际应用中,线程数量一般会设置为“CPU核数 + 1" ,这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以顶上,从而保证CPU利用率。

♠ I/O密集型

对于I/O密集型的计算场景,比如前面的例子中,如果 CPU 计算和 I/O 操作的耗时是1:1,那么2个线程是最合适的。如果 CPU 计算和 I/O 操作的耗时是1:2,那多少个线程合适呢?是3个线程,如图所示:CPU在A、B、C三个线程之间切换,对于线程A,当CPU 从B、C线程切换回来时,线程A正好执行完I/O操作。这样 CPU 和 I/O 设备的利用率都达到了100%。
                 在这里插入图片描述
通过上面这个例子,我们发现,对于I/O 密集型计算场景,最佳线程数是与程序中CPU计算和I/O操作的耗时比相关的。
可以总结一个公式:最佳线程数 = 1 + (I/O 耗时 / CPU耗时)

我们令 R = I/O 耗时 / CPU耗时,综合上图,可以理解成:当线程A执行IO操作时,另外 R 个线程正好执行完各自的CPU计算。这样CPU利用率就达到了100%。

对于多核CPU,等比扩大即可:最佳线程数 = CPU核数 * [ 1 + (I/O 耗时 / CPU耗时)]

有收获的话,点个赞吧~

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章