先举例子来理解这2个概念的区别。程序员
老师让两个同窗去办公室谈话。若是这两同窗(进程)是并列跨过办公室门(CPU)的,那么就是并行。若是同窗A先进同窗B后进入(或者先B后A),或者两人并列同时进入,可是在办公室外的路人甲(用户)看来,同窗A和同窗B同时都在办公室内,这是并发。web
其实这个例子不合理,由于真正的并行是多核CPU下的概念,但上面这个简单的例子很是有助于理解。算法
若是举例要精确一点,那么大概是这样的:进办公室有两个门(两CPU),若是两同窗分别从不一样的门进入,无论前后性,二者互相独立,那么是并行;若是两同窗无论以什么方式进入,在路人甲看来,他两同时都在办公室内,就是并发。服务器
我不信到如今还不理解并发和并行。数据结构
为何操做系统上能够同时运行多个程序而用户感受不出来?多线程
这是由于不管是单CPU仍是多CPU,操做系统都营造出了能够同时运行多个程序的假象。实际的过程操做系统对进程的调度以及CPU的快速上下文切换实现的:每一个进程执行一会就先停下来,而后CPU切换到下个被操做系统调度到的进程上使之运行。由于切换的很快,使得用户认为操做系统一直在服务本身的程序。并发
再来解释并发就容易理解多了。异步
并发(concurrent)指的是多个程序能够同时运行的现象,更细化的是多进程能够同时运行或者多指令能够同时运行。但这不是重点,在描述并发的时候也不会去扣这种字眼是否精确,并发的重点在于它是一种现象。并发描述的是多进程同时运行的现象。但实际上,对于单核心CPU来讲,同一时刻只能运行一个进程。因此,这里的"同时运行"表示的不是真的同一时刻有多个进程运行的现象,这是并行的概念,而是提供一种功能让用户看来多个程序同时运行起来了,但实际上这些程序中的进程不是一直霸占CPU的,而是执行一会停一会。函数
因此,并发和并行的区别就很明显了。它们虽然都说是"多个进程同时运行",可是它们的"同时"不是一个概念。并行的"同时"是同一时刻能够多个进程在运行(处于running),并发的"同时"是通过上下文快速切换,使得看上去多个进程同时都在运行的现象,是一种OS欺骗用户的现象。操作系统
实际上,当程序中写下多进程或多线程代码时,这意味着的是并发而不是并行。并发是由于多进程/多线程都是须要去完成的任务,不并行是由于并行与否由操做系统的调度器决定,可能会让多个进程/线程被调度到同一个CPU核心上。只不过调度算法会尽可能让不一样进程/线程使用不一样的CPU核心,因此在实际使用中几乎老是会并行,但却不能以100%的角度去保证会并行。也就是说,并行与否程序员没法控制,只能让操做系统决定。
再次注明,并发是一种现象,之因此能有这种现象的存在,和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() }
这两种都是串行的代码模式。画图描述:
并行意味着能够同时取得多个任务,并同时去执行所取得的这些任务。并行模式至关于将长长的一条队列,划分红了多条短队列,因此并行缩短了任务队列的长度。
正如前面所举的两同窗进办公室的例子,串行的方式下,必须1个同窗进入后第二个同窗才进入,队列长度为2,而并行方式下能够同时进入,队列长度减半了。
并行的效率从代码层次上强依赖于多进程/多线程代码,从硬件角度上则依赖于多核CPU。
对于单进程/单线程,因为只有一个进程/线程在执行,因此尽管同时执行所取得的多个任务,但实际上这个进程/线程是不断的在多任务之间切换,一会执行一下这个,一会执行一下那个,就像是一我的在不一样地方之间来回奔波。因此,单进程/线程的并行,效率比串行更低。
对于多进程/多线程,各进程/线程均可以执行各自所取得的任务,这是真正的并行。
可是,还须要考虑硬件层次上CPU核心数,若是只有单核CPU,那么在硬件角度上这单核CPU一次也只能执行一个任务,上面多进程/多线程的并行也并不是真正意义上的并行。只有多核CPU,而且多进程/多线程并行,才是真正意义上的并行。
以下图,是多进程/多线程(2个工做者)的并行:
并发表示多个任务同时都要执行的现象,更详细的概念前面已经说面的够具体了。
其实,不少场景下都会使用并发的概念。好比同时500个http请求涌向了web服务器,好比有时候说并发数是1000等。
有时候也将并发当成任务,好比500并发数意味着500个任务,表示的是在一个特定的时间段内(约定俗成的你们认为是1秒)能够完成500个任务。这500个任务能够是单进程/单线程方式处理的,这时表示的是并发不并行的模式(coroutine就是典型的并发不并行),即先执行完一个任务后才执行另外一个任务,也能够是多进程/多线程方式处理的,这时表示的是并发且并行模式。
要解决大并发问题,一般是将大任务分解成多个小任务。很典型的一个例子是处理客户端的请求任务,这个大任务里面包含了监听并创建客户端的链接、处理客户端的请求、响应客户端。但基本上全部这类程序,都将这3部分任务分开了:在执行任何一个小任务的时候,均可以经过一些手段使得能够执行其它小任务,好比在处理请求的时候,能够继续保持监听状态。
因为操做系统对进程的调度是随机的,因此切分红多个小任务后,可能会从任一小任务处执行。这可能会出现一些现象:
可能出现一个小任务执行了屡次,还没开始下个任务的状况。这时通常会采用队列或相似的数据结构来存放各个小任务的成果。好比负责监听的进程已经执行了屡次,创建了多个链接,可是尚未调度处处理请求的进程去处理任何一个请求。
能够多进程/多线程的方式并行执行这些小任务。也能够单进程/单线程执行这些小任务,这时极可能要配合多路复用才能达到较高的效率
看图很是容易理解:
上图中将一个任务中的三个步骤取柴、运柴、卸柴划分红了独立的小任务,有取柴的老鼠,有运柴的老鼠,有卸柴烧火的老鼠。
若是上图中全部的老鼠都是同一只,那么是串行并发的,若是是不一样的多只老鼠,那么是并行并发的。
并行和串行:
并发: