一、基本概念

1. 进程、线程与协程

  • 进程:进程是操作系统分配资源的基本单位,它包含了一个程序的执行实例及其用到的系统资源(如内存、文件、设备等)。每个进程有自己独立的内存空间,不同的进程通过进程间通信来交换信息。
  • 线程:线程是 CPU 调度和执行的基本单位,是包含在进程中(比进程更小)的能独立运行的最小单位,线程又称为轻量级进程。每个进程可以有一个或多个线程,进程中的各个线程共享该进程的内存空间。线程没有独立的内存空间,只拥有运行时必不可少的资源,如程序计数器、寄存器和栈。
  • 协程:协程又称微线程,是用户态的轻量级线程,其调度由用户控制。每个协程拥有自己的寄存器上下文和栈。当协程切换时,将寄存器上下文和栈保存起来,协程切回来时,恢复之前保存的寄存器上下文和栈。协程间的切换不涉及内核切换的开销,协程切换比线程切换效率更高。
概念 进程 线程 协程
资源占用 独立的内存地址空间(保存数据、代码等资源) 共享所属进程的内存地址空间 共享所在线程的内存地址空间
创建开销 较高,需要进行大量的资源分配和初始化 较低,因为共享进程资源 最低,通常由程序内部控制,不需要操作系统介入
切换开销 较高,涉及 CPU 的上下文切换 较低,涉及内核线程的上下文切换 最低,通常由程序控制,不需要 CPU 和内核上下文切换
通信方式 通过进程间通信(IPC),如管道、消息队列、共享内存等 通过共享内存 通过参数传递,或共享数据结构
应用场景 不同的程序或服务 多个子任务并发执行 异步编程,提高性能
竞态条件 使用锁、信号量等同步机制 使用锁、信号量等同步机制 通常不用加锁,协程之间没有竞争关系

举个通俗点的例子来理解下这些干巴巴的概念:

比如我们要准备一顿晚饭,做饭这项活动就可以视为一个进程,炉灶和食材是这个进程可以利用的资源。

炒菜和煮饭可以视为两个子线程,它们共用炉灶和食材资源,当资源紧缺(比如只有一个炉灶)时,会存在竞争关系。

晚饭计划一菜一汤,当炖汤时,我们可以一边等,一边准备下一道菜的工序,比如摘菜、洗菜、切菜。每项工序完成,我们还要去看看炖汤的进展。这个切换操作就是协程。

2. 并发和并行

  • 并发:指在同一时间段内宏观上有多个程序在同时运行,但在单核 CPU 中,这些程序是交替执行的,同一时刻,只有一条指令在执行。
  • 并行:指在同一时刻,有多条指令在多个处理器上同时执行,需要有硬件支持,比如多核 CPU、GPU 等,是真正意义上的同时运行。

举个例子,在一个便利店有两队(红队和蓝队)顾客排队买单。

如果只有一个收银员,那这个收银员只能交替给两队顾客结账,在同一时刻,只能有一个顾客在买单,要么来自红队,要么来自蓝队。

如果有两个收银员,那么每个收银员可以对接一队顾客。在同一时刻,蓝队和红队可能同时有顾客在买单。

3. 同步和异步

  • 同步:在同步模式下,当程序执行一个操作 A,必须等这个 A 操作返回结果后,才能执行下一个操作 B。当操作 A 需要执行很长时间才能完成时,操作 B 需要陷入长久的等待,程序出现阻塞。
  • 异步:在异步模式下,当程序执行一个操作 A,不会等 A 操作完成,而是立即执行下一个操作 B。当 A 操作执行完后,程序通过某种机制(例如回调函数、事件等)接收 A 操作的结果,再进行后续处理。异步使得在等待操作 A 运行期间,程序可以执行其他操作,提高了运行效率。

举个例子,肚子饿了去搞吃的,有两种餐馆:

一种是没有服务员上菜的,只能在窗口排队等着,现点现做,拿到餐品才能去找座位。这种就是同步模式,等取餐了才能去找座位。

另一种是有服务员负责传餐的,点完餐品,可以先去找个位子坐下,等服务员上菜就行。这种就是异步模式,等餐的过程中可以去找座位。

异步和协程的想法很相似,当某一项任务需要花费很长时间时,可以先转去做别的任务,避免干等。

协程可以作为实现异步的一种技术,它提供了一种简洁好用的异步模型,避免了多层嵌套的回调函数(回调地狱)导致代码变得极其复杂,难以理解和维护。

二、python 并发实践

1. 多进程与进程池

2. 多线程与线程池

3. 协程

4. 异步

三、参考资料

  1. 一文读懂什么是进程、线程、协程
  2. 一篇文章搞懂 Python 中的进程和线程
  3. python3-cookbook 并发编程
  4. 2w 字 + 40 张图带你参透并发编程