原文地址:https://mp.weixin.qq.com/s/1beoHvvOBK-s_TXjTYWSYg
前言:并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。它们最关键的点就是:是否是『同时』。
概念理解
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
所以我认为它们最关键的点就是:是否是『同时』。
图解一
Erlang 之父 Joe Armstrong用一张5岁小孩都能看懂的图解释了并发与并行的区别
并发:两队人交替使用咖啡机,如果前面的人有事先去厕所了,下一个人继续使用,不会等待这个人
并行:直接多了一个咖啡机,两队人使用两个咖啡机
此处的咖啡机可以看做CPU,等待去咖啡的人可以看作任务
图解二
任务描述
如图:
任务是将左边的一堆柴全部搬到右边烧掉,每个任务包括三个过程:取柴,运柴,放柴烧火。
这三个过程分别对应一个函数:
func get { geting }
func carry { carrying }
func unload { unloading }
串行模式
串行表示所有任务都一一按先后顺序进行。串行意味着必须先装完一车柴才能运送这车柴,只有运送到了,才能卸下这车柴,并且只有完成了这整个三个步骤,才能进行下一个步骤。
和稍后所解释的并行相对比,串行是一次只能取得一个任务,并执行这个任务。
假设这堆柴需要运送4次才能运完,那么当写下的代码类似于下面这种时,那么就是串行非并发的模式:
for(i=0;i<4;i++){
get()
carry()
unload()
}
或者,将三个过程的代码全部集中到一个函数中也是如此:
func task {
geting
carrying
unloading
}
for(i=0;i<4;i++){
task()
}
这两种都是串行的代码模式。画图描述:
并行模式
并发
上图中将一个任务中的三个步骤取柴、运柴、卸柴划分成了独立的小任务,有取柴的老鼠,有运柴的老鼠,有卸柴烧火的老鼠。
如果上图中所有的老鼠都是同一只,那么是串行并发的,如果是不同的多只老鼠,那么是并行并发的。
并发程序的执行效率
我们思考一个问题:多线程就一定能提高处理速度吗?(多个goroutine的效率一定会高吗?)
CPU运行时,通过将于运行时间分片,通过调度来分配给各个进程线程来执行。因为时间片非常短,所以常常让人误以为是多个线程是同时并行执行。
使用多线程来提高程序处理速度,其本质是提高对CPU的利用率。主要是两个方面
- 阻塞等待时充分利用CPU 当程序发生阻塞的操作时候,例如IO等待,CPU将就空闲下来了。而使用多线程,当一些线程发生阻塞的时候,另一些线程则仍能利用CPU,而不至于让CPU一直空闲。
- 利用CPU的多核并行计算能力 现在的CPU基本上都是多核的。使用多线程,可以利用多核同时执行多个线程,而不至于单线程时一个核心满载,而其他核心空闲。
再次回到我们最初的问题:多线程就一定能提高处理速度吗?
显然不一定,我们都知道计算机程序基本上可以分为两类:
- IO密集型程序
- CPU密集型程序
当程序偏计算型的时候,盲目启动大量线程来并发,并不能提高处理速度,反而会降低处理速度。因为在多个线程进行切换执行的时候会带会一定的开销。其中有上下文切换开销,CPU调度线程的开销,线程创建和消亡的开销等。其中主要是上下文切换带来的开销。