Go并发编程-并发与并行

一、前言

很多人会把并发与并行的概念弄混,有时候说并发有时候说并行,那么两者究竟有啥区别那,本节我们就来澄清下两者概念。

二、并发与并行

首先我们先看下什么是进程,比如你打开的微信app就是一个进程、打开的手淘App、记事本程序就是一个进程,在高级编程语言中你启动main函数运行后其实也启动了一个进程。

进程(Process)是计算机中的程序在某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,也就是说操作系统是以进程为单位进行资源分配的,但是 CPU 这个资源却比较特殊,CPU 的分配是以线程为单位的,这是因为具体占用 CPU 运行的是进程中的线程。一个进程中至少有一个线程,比如启动main函数运行的时候,main函数所在线程就是该进程的主线程,在go中main函数所在线程的生命周期等于进程的生命周期,当main函数所在线程运行结束后,进程就退出了。Go并发编程-并发与并行

如上图进程中包含进程控制块(PCB)和用户地址空间和很多线程:

  • 其中PCB用来保存进程的控制信息,比如每个pc上全局唯一的进程编号,进程的上下信息(当进程执行上下文切换时候要保存当前进程的上下文信息),控制信息(比如用来管理调度的进程的状态、进程间通信的信号量或消息队列信息、cpu资源的使用情况等)

  • 用户地址空间则用来保存进程运行时需要的代码和数据等信息。

  • 每个进程中可以有多个线程,每个线程有自己的用户栈资源,用于存储该线程的局部变量,这些局部变量是该线程私有的,其它线程是访问不了的,另外栈还用来存在当前线程的函数调用栈帧信息,用户栈用来存放用户程序执行的一些信息,系统栈用来存放native调用的执行信息。

并发是指同一个时间段内多个任务同时交叉执行,并且都没有执行结束,而并行是说在单位时间内多个任务同时在执行,并发任务强调在一个时间段内同时执行,而一个时间段有多个单位时间累积而成,所以说并发的多个任务在单位时间内不一定同时在执行。

在单个cpu的时代多个任务同时运行都是并发,这是因为cpu同时只能执行一个任务,单个cpu时代多任务是共享一个cpu的,当一个任务占用cpu运行时候,其它任务就会被挂起,当占用cpu的任务时间片用完后,会把cpu让给其它任务来使用,所以在单cpu时代多线程编程是意义不大,并且线程间频繁的上下文切换还会带来开销。

如下图单个cpu上运行两个线程,可知线程A和B是轮流使用cpu进行任务处理的,也就是同时CPU只在执行一个线程上面的任务,当前线程A的时间片用完后会进行线程上下文切换,也就是保存当前线程的执行线程,然后切换线程B占用cpu运行任务,线程A和线程B走走停停、交叉运行的,我们就说线程A和线程B是并发运行的:

Go并发编程-并发与并行

如下图双cpu时候,线程A和线程B各自在自己的CPU上执行任务,实现了真正的并行运行。

Go并发编程-并发与并行

而在多线程编程实践中线程的个数往往多于CPU的个数,所以平时都是称多线程并发编程而不是多线程并行编程。

三、为什么要多线程并发编程

随着多核CPU时代的到来,打破了单核CPU对多线程效能的限制,多个CPU意味着每个线程可以使用自己的CPU运行,这减少了线程上下文切换的开销,并且随着对应用系统性能和吞吐量的要求提高,还有海量数据处理和请求的要求,都对高并发编程有着迫切的需求。

四、总结

并发强调多个任务在一个时间段内走走停停,交叉运行,并不要求多个任务在单位时间内同时运行,例如在单cpu上运行多个任务或者在任务数大于cpu个数时候使用分时复用cpu策略运行任务就是并发运行;而并行则强调多个任务同时运行。