我们都知道计算机的核心是CPU,它承担了所有的计算任务,而操作系统是计算机的管理者,它负责任务的调度,资源的分配和管理,统领整个计算机硬件;应用程序是具有某种功能的程序,程序是运行于操作系统之上的。
进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。进程一般由程序,数据集合和进程控制块三部分组成。程序用于描述进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时所需要的数据和工作区;程序控制块(PCB)包含进程的描述信息和控制信息,是进程存在的唯一标志。
进程具有的特征:
在早期的操作系统中并没有线程的概念,进程是拥有资源和独立运行的最小单位,也是程序执行的最小单位。任务调度采用的是 时间片轮转 的 抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。
后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。每个进程都有自己的地址空间,即进程空间,在网络或多用户换机下,一个服务器通常需要接收大量不确定数量用户的并发请求,为每一个请求都创建一个进程显然行不通(系统开销大,响应用户请求效率低),因此操作系统中线程概念被引进。
于是就发明了线程,线程是程序执行中一个 单一的顺序控制流程,是程序执行流的最小单元,是 处理器调度和分派的基本单位 。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。一个标准的线程由线程ID,当前指令指针PC,寄存器和堆栈组成。而进程由内存空间(代码,数据,进程空间,打开的文件)和一个或多个线程组成。
如果说 进程和进程 之间相当于 程序与程序 之间的关系,那么 线程与线程 之间就相当于程序内的 任务和任务 之间的关系。所以线程是依赖于进程的,也称为 「微进程」 。它是 程序执行过程中的最小单元 。
一个程序内包含了多种任务。打个比方,用播放器看视频的时候,视频输出的画面和声音可以认为是两种任务。当你拖动进度条的时候又触发了另外一种任务。拖动进度条会导致画面和声音都发生变化,如果进程里没有线程的话,那么可能发生的情况就是:拖动进度条->画面更新->声音更新。你会明显感到画面和声音和进度条不同步。
但是加上了线程之后,线程能够共享进程的大部分资源,并参与CPU的调度。意味着它能够在进程内进行切换,实现 并发,从而反馈到使用上就是拖动进度条的同时,画面和声音都同步了。所以我们经常能听到的一个词是 多线程,就是 把一个程序分成多个任务去跑,让任务 更快 处理。不过线程和线程之间由于某些资源是独占的,会导致 锁 的问题。例如Python的GIL多线程锁。
线程是程序执行的最小单位(CPU上真正运行的是线程),而进程是操作系统分配资源的最小单位。进程拥有自己的资源空间,一个进程包含若干个线程,线程与CPU资源分配无关,多个线程共享同一进程内的资源。
一个进程由一个或多个线程组成,线程是一个进程中的不同任务;
进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信号等),某进程内的线程在其他进程不可见;
调度和切换:线程上下文切换比进程上下文切换要快得多,线程的调度与切换比进程快很多。
线程和进程关系示意图:
上图中,表示一个进程内有三个线程。
下图是一个进程内三个线程并发运行的示意图。
总之,线程和进程都是一种抽象的概念,线程是一种比进程还小的抽象,线程和进程都可用于实现并发。
在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位,它相当于一个进程里只有一个线程,进程本身就是线程。所以线程有时被称为轻量级进程。
后来,随着计算机的发展,对多个任务之间上下文切换的效率要求越来越高,就抽象出一个更小的概念-线程,一般一个进程会有多个(也可以是一个)线程。
总结:进程和线程的区别
大部分操作系统的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行(并发)。任务执行的一小段时间叫做时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态,等待下一个属于它的时间片的到来。这样每个任务都能得到执行,由于CPU的执行效率非常高,时间片非常短,在各个任务之间快速地切换,给人的感觉就是多个任务在“同时进行”,这也就是我们所说的并发。
为何要求效率高时,不使用多进程而是使用多线程?
由于网络服务器中对并行处理(提高效率,更快)的需求,线程成为越来越重要的编程模型。因为:
3. 多线程之间比多进程之间 更容易共享数据。(不同进程的进程空间是彼此独立的,而一个进程内的多个线程之间共享本进程的资源)
4. 线程一般来说都比进程 更高效。(线程廉价,线程启动比较快,退出比较快)
关于进程和线程更详细的知识请参考:五分钟扫盲:进程与线程基础必知
协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是协程:协程是一种用户态的轻量级线程。
从技术的角度来说,“协程就是你可以暂停执行的函数”。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。所以协程相比线程进一步降低了上下文切换的开销。
协程的优点:
高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于 高并发 处理。
缺点:
并发和并行最开始都是操作系统中的概念,表示的是 CPU 执行多个任务的方式 。
并发和并行指的是多个任务(进程)执行时之间的关系。
如果多个进程是交替着在一个处理器上执行,那么他们之间的关系就是并发;
如果多个进程各自独立地在多个处理器上(一个进程一个处理器)同时执行,那么他们之间的关系就是并行。
所谓 “同时” 进行,其实并不是同时,而是时间片轮转。
我们现在使用的windows操作系统,是可以 “同时” 做很多件事儿的。比如我们可以一边看电影,一边聊QQ;一边听歌,一边打游戏。
但是,这所谓的"同时",在操作系统底层可能并不是真正的意义上的"同时"。
实际上,对于 单CPU 的计算机来说,在CPU中,同一时间是只能干一件事儿的 。为了看起来像是“同时干多件事”,Windows这种操作系统是把CPU的时间划分成长短基本相同的时间区间,即 “ 时间片 ”,通过操作系统的管理,把这些时间片依次轮流地分配给各个应用 使用。
这样,给用户的感觉是他在同时的进行听歌和打游戏,实际上,在操作系统中,CPU是在游戏进程和音乐播放器进程之间来回切换执行的。
操作系统时间片的使用是有规则的:某个作业在时间片结束之前,整个任务还没有完成,那么该作业就被暂停下来,放弃CPU,等待下一轮循环再继续做。此时,通过 上下文切换(保存当前进程的上下文、恢复新进程的上下文) ,CPU又分配给另一个作业去使用。
多一嘴:执行上下文切换时,执行的是内核代码。也就是说,上下文切换工作是由内核代码控制的。
从一个进程到另一个进程的转换是由操作系统内核(kernel)管理的。
内核是操作系统代码常驻主存的部分。
我们把目光聚焦在CPU的执行上,把这个过程放大的话,CPU就好像是一个电话亭。多个用户并不是同一时间在使用这个电话亭中的电话的,而是轮流使用的。
由于计算机的处理速度很快,只要时间片的间隔取得适当,那么一个用户作业从用完分配给它的一个时间片到获得下一个CPU时间片,中间有所 “停顿” ,但用户察觉不出来。
所以,在单CPU的计算机中,我们看起来“同时干多件事”,其实是通过CPU 时间片 技术,并发完成的。
总结并发:
并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在 同一个处理机 上运行。
就想前面提到的操作系统的时间片分时调度。打游戏和听音乐两件事情在同一个时间段内都是在同一台电脑上完成了从开始到结束的动作。那么,就可以说听音乐和打游戏是并发的。
并行(Parallel),当系统有 一个以上CPU 时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程 互不抢占CPU资源,可以 同时进行,这种方式我们称之为并行(Parallel)。
这里面有一个很重要的点,那就是系统 要有多个CPU才会出现并行。在有多个CPU的情况下,才会出现真正意义上的『同时进行』。
并发是指在一段时间内宏观上多个程序同时运行。并行指的是同一个时刻,多个任务确实真的在同时运行。
1>处理器数量:
一个人“同时” 做多件事,就是并发;多个人同时做多件事,就是并行。
2>时间上:
并发是 逻辑上 的同时发生(simultaneous),而并行是 物理上 的同时发生。
并发是 一段时间内 同时推进,并行是 某一时刻 真的同时发生。
并发性(concurrency):又称共行性,是指能 处理多个同时性活动的能力,并发事件之间不一定要同一时刻发生,并发事件是一段时间内一起推进。
并行(parallelism):是指某一时刻同时发生的两个事件。
来个比喻:并发和并行的区别就是 一个人同时吃三个馒头 和 三个人同时吃三个馒头 。
3>效率上:
下图反映了一个 包含8个操作的任务 在一个有 两核心 的CPU中创建四个线程运行的情况。假设 每个核心有两个线程 ,那么 每个CPU中两个线程 会 交替并发 ,两个CPU之间 的操作会 并行 运算。
单就一个CPU而言,两个线程可以解决线程阻塞造成的不流畅问题,其本身运行效率并没有提高(两个线程之间只是交替),多CPU的并行运算才真正解决了运行效率问题,这也正是并发和并行的区别。
总结:并发和并行的区别
并发,指的是多个事情,在同一时间段内同时发生了。
并行,指的是多个事情,在同一时间点上同时发生了。
并发的多个任务之间是互相抢占资源的。
并行的多个任务之间是不互相抢占资源的。
只有在 多CPU 的情况中,才会发生并行 。否则,看似同时发生的事情,其实都是并发执行的。
就像上面这张图,只有 一个咖啡机 的时候,一台咖啡机其实是在 并发 被使用的。而有 多个咖啡机 的时候,多个咖啡机之间才是 并行 被使用的。