理解并发和并行的区别

任务描述

如图:

任务是将左边的一堆柴全部搬到右边烧掉,每个任务包括三个过程:取柴,运柴,放柴烧火。

这三个过程分别对应一个函数:

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()
}

这两种都是串行的代码模式。画图描述:

并行模式

并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度

并行的效率从代码层次上强依赖于多进程/多线程代码,从硬件角度上则依赖于多核CPU。

对于单进程/单线程,由于只有一个进程/线程在执行,所以尽管同时执行所取得的多个任务,但实际上这个进程/线程是不断的在多任务之间切换,一会执行一下这个,一会执行一下那个,就像是一个人在不同地方之间来回奔波。所以,单进程/线程的并行,效率比串行更低。

对于多进程/多线程,各进程/线程都可以执行各自所取得的任务,这是真正的并行。

但是,还需要考虑硬件层次上CPU核心数,如果只有单核CPU,那么在硬件角度上这单核CPU一次也只能执行一个任务,上面多进程/多线程的并行也并非真正意义上的并行。只有多核CPU,并且多进程/多线程并行,才是真正意义上的并行。

如下图,是多进程/多线程(2个工作者)的并行:

并发

并发是一种现象,表示多个任务同时涌入的情况。

比如同时500个http请求涌向了web服务器。实际上对于CPU来说,操作系统上要执行的那些进程也是一种并发现象:这些等待执行的进程全部都需要CPU。

区分并发和并行是很简单的,并发是一种现象,并行是一种执行模式,是处理并发现象的一种方案

有时候也将并发当成任务,比如500并发数意味着500个任务,这500个任务可以是单进程/单线程方式处理的,这时表示的是并发不并行的模式(coroutine就是典型的并发不并行),也可以是多进程/多线程方式处理的,这时表示的是并发且并行模式。

要解决大并发问题,通常是将大任务分解成多个小任务。由于可能会从任一小任务处执行:

  • 可能出现一个小任务执行了多次,还没开始下个任务的情况。这时一般会采用队列或类似的数据结构来存放各个小任务的成果
  • 可能出现还没准备好第一步就执行第二步的可能。这时,一般采用多路复用或异步的方式,比如只有准备好产生了事件通知才执行某个任务
  • 可以多进程/多线程的方式并行执行这些小任务,也可以单进程/单线程执行这些小任务,这时很可能要配合多路复用才能达到较高的效率

看图非常容易理解:

上图中将一个任务中的三个步骤取柴、运柴、卸柴划分成了独立的小任务,有取柴的老鼠,有运柴的老鼠,有卸柴烧火的老鼠。

如果上图中所有的老鼠都是同一只,那么是串行并发的,如果是不同的多只老鼠,那么是并行并发的

总结

并行和串行:

  • 串行:一次只能取得一个任务并执行这一个任务
  • 并行:可以同时通过多进程/多线程的方式取得多个任务,并以多进程或多线程的方式同时执行这些任务
  • 注意点:
    • 如果是单进程/单线程的并行,那么效率比串行更差
    • 如果只有单核cpu,多进程并行并没有提高效率
    • 从任务队列上看,由于同时从队列中取得多个任务并执行,相当于将一个长任务队列变成了短队列

并发:

  • 并发是一种现象:一次性涌入多个需要被处理的任务
  • 这些任务可能是并行执行的,也可能是串行执行的
  • 解决大并发的一个思路是将大任务分解成多个小任务:
    • 以多进程/多线程并行的方式去执行这些小任务
    • 以单进程/单线程配合多路复用执行这些小任务

猜你喜欢

转载自www.cnblogs.com/f-ck-need-u/p/11004784.html