Pulpcode

捕获,搅碎,拼接,吞咽

0%

对于单核 cpu 而言, 开多线程的目的难倒只能是为了防止阻塞么? 

之前对多线程的一些问题一直无法理解,所以在各个论坛提出了此问题,这里将好的回复收集与此。

原问题

对于单核 cpu 而言,开多线程的目的难倒只能是为了防止阻塞么?

以下是一些单核 cpu 多线程的疑问,求解答(都指单核)。

  1. 如果一个进程有 n 个任务要处理,因为终究是在一个 cpu 上跑,所以这 n 个任务在一个线程还是多个线程上跑,执行的总时间是一样的(多线程,线程切换可能更浪费时间)?
  2. 是否进程开多线程就能抢到更多的 cpu 时间, python 这种带 GIL 的估计是没戏了,那么 java 呢?
  3. 自己抢到更多 cpu ,机器上的其它程序不就 cpu 时间少了么?是因为 cpu 大部分时间都是空闲的,不怕抢?还是因为在做应用层开发的时候,是不用考虑其它程序能不能抢到 cpu 时间的。
  4. 一个进程所有线程能抢到的时间片总和是有最大值吗?一个线程一次能拿到多长的 cpu 时间?

综上,我的最大疑问就是:对于单核 cpu 而言,开多线程难倒只能防止阻塞么?

V2EX

codlist

  1. 如果一个进程执行 N 个任务,而这些任务又属于 I/O 型的任务,通过 CPU 的调度是可以提高效率的,通常网络 I/O 阻塞的时间比线程 context switch 的时间要长多了。
  2. 在 Linux 下线程进程的调度策略相同,如果一个进程中包含更多线程的话,我猜会拿到更多的执行时间。 CPython 的 GIL 只是给多线程的执行加了锁,限制了多线程的并行执行,单核下 GIL 应该就跟没有一样。
  3. CPU 的计算速度太快了,进程的调度已经比较完善,通常应用层面程序不需要考虑底层进程的调度对程序的影响,但是要考虑程序 CPU 资源的消耗,写一个死循环跑一会儿 CPU 都能煎鸡蛋了。大多数情况下 CPU 的计算都是不饱和的,通常服务器 CPU 利用率达到 80%的时候就要扩容了。
  4. 没有听过类似这种限制,太细的东西不清楚。开多线程并一定是为了性能,很多场景下你的业务逻辑是并发的,所以你的程序逻辑也是并发的。

leavic

我仅仅从一个 5 年单片机码农所理解的角度回答一下这些问题:

  1. 如果一个进程有 n 个任务要处理,因为终究是在一个 cpu 上跑,所以这 n 个任务在一个线程还是多个线程上跑,执行的总时间是一样的(多线程,线程切换可能更浪费时间)?

A :对于单核 CPU 来说,所谓的线程,就是 task , CPU 就是依靠调度器在这 N 个 task 之间来回切换,同一时刻只有一个 task 在运作。

  1. 是否进程开多线程就能抢到更多的 cpu 时间, python 这种带 GIL 的估计是没戏了,那么 java 呢?

A :这个涉及到操作系统的设计原理, windows 和 linux 本质上都不是实时系统,如何分配 CPU 时间在不同的操作系统上有不同的规则,一般来说是按优先级分配。但你一个人开了多个任务,总概率上讲分配到更多 CPU 资源的概率是更高的。

  1. 自己抢到更多 cpu ,机器上的其它程序不就 cpu 时间少了么?是因为 cpu 大部分时间都是空闲的,不怕抢?还是因为在做应用层开发的时候,是不用考虑其它程序能不能抢到 cpu 时间的。

A: 这个取决于你应用到底能 CPU 密集到什么程度,对一般的应用来说,你是不需要担心别的程序没有 CPU 资源可用的,唯一需要注意的是避免进入死循环,在单片机中,中断里出现一个死循环的结果就是占用全部 CPU 资源。

  1. 一个进程所有线程能抢到的时间片总和是有最大值吗?一个线程一次能拿到多长的 cpu 时间?
    综上,我的最大疑问就是:对于单核 cpu 而言,开多线程难倒只能防止阻塞么?

A:这个依然取决于操作系统的设计。对于单核 CPU 而言,多线程是几乎唯一合理的利用 CPU 资源的方式,一颗 CPU 如果只有一个线程,那基本就是个弱智级别的 CPU 。

sgissb1

首先说一下一个进程里面为何要开多线程问题,和 cpu 是否多核,或者主板是否多路 cpu 关系是有的,但也是有限定条件的,具体内容如下:

  1. 由于部分 api 不支持异步方式执行,仅仅支持同步执行。例如读取一个文件,当每次读取的文件的数据很大时,内存缓存中没有足够的内存提交给调用者,这时 api 或者操作系统会代用户去从磁盘上读取文件,而磁盘相对于内存和 cache 来说属于慢速设备,并且在操作系统调度层面,对于此类操作是,现成被阻塞住(即挂起),然后转为执行其他任务。
    为了让整个进程不进入假死(僵死)情况,需要在开一个线程来维持其他任务的执行。
  2. 在多路 cpu 的系统中,多线程可以提高任务处理的效率。即理想情况下,每个线程独占一路 cpu 处理能。
  3. 特殊多路系统,在非对称多路系统中,每个线程(进程)都执行在不同的核上,所访问的硬件资源不同,所以需要开多个线程。

至于你说的抢 cpu 执行时间问题,要分操作系统看,目前我们常用的 windows 和 linux 都是抢占式操作系统。对于 linux 我不是很熟,对 windows 略知一二:

  1. 操作系统调度粒度是线程+进程。即操作系统调度的最小单位是线程,但是否决定该线程执行,除了看线程执行时间和未执行间隔以外,还需要看总进程内的执行情况。
    其中还存在进线程优先级问题,中断优先级, cpu 执行平衡问题,就近执行原则等。
  2. 一个进程中,线程并非越开越多就好。因为非分时的非实时操作系统的任务调度周期很有可能是不准的,操作系统是否要打断一个线程的执行,转而交给另外一个线程执行,除了调度周期到来的调度扫描,还有任务执行的优先级, IRQL 级别,线程处于什么状态等。
  3. 过多的线程不仅不能抢到有效的 cpu 执行时间,还会导致线程环境切换的浪费。因为每次线程的切换其中伴随的态转换,以及堆栈移动、寄存器恢复、内存调度等问题。频繁切换线程,只会得不偿失,不管是 python 或者 java 或者 c++或者 c#都是一样的,因为是操作系统在管理线程执行。

至于你提出的第四个疑问,在单核 cpu 里面开多线程的事情。我只能和你说,设计决定一切。

知乎

罗然

以下回答均针对单核CPU

问题1概括下来就是很多人喜欢争论的多线程究竟能不能提高性能?

首先,回答是“能或者不能”。至于“不能”你已经理解了,那么我来说说为什么多线程“能”提高性能。要知道一个作业可不总是CPU密集型的,必然穿插着大量的IO调用在其中。而IO的一个特性就是阻塞等待。这个阻塞等待的时间消耗往往是远远大于线程切换所消耗的时间的,如果你要访问10个url获取接口内容,假如一次http访问平均阻塞时间大概是1s,那么你是一个一个的线性访问快还是10个线程访问快?相信不用算也知道多线程肯定更快。

最后就可以得出结论,多线程在CPU密集型的作业下的确不能提高性能甚至更浪费时间,但是在IO密集型的作业下则可以提升性能(或者更准确点说叫平均响应时间)。

问题2,进程是最小作业单元,跟进程内开多少线程都无关,CPU对进程的调度是统一的。所以多线程无法促进进程被CPU青睐。python的GIL也是只在CPU密集型的作业下显现的,通常的业务充斥着大量的IO,所以如果你不是做科学计算,那么放心大胆的使用多线程吧。

问题3,4,虽说操作系统有自己的调度策略,比如争抢,时间片轮转,但是用户态进程仅仅想通过自身应用级的代码实现如多线程等手段企图加大自身的CPU调度权重是不行的,不过自身的线程是可以实现优先级设置的。也就是说CPU给你整个进程的资源是有限且无法更改的,但是这些资源如何分配你是可以参与的,比如设置线程的优先级,也只是参与不能主导CPU在某个线程的调度时间,这个是无法控制的。跟当时的系统压力有关。

综上,你的问题提到了“阻塞”,这是服务端编程永恒的经典话题。不管是多进程,多线程,还是协程,大多都是致力于解决IO问题,说白了都是怎么样把阻塞变成非阻塞的手段。

附录:

v2ex原帖: http://www.v2ex.com/t/235443#reply33

知乎原帖:http://www.zhihu.com/question/37396742