Skip to content
On this page

正式定义

协程是程序组件的一种,它允许执行被挂起,并在之后从挂起点恢复。它是比线程更轻量级的用户态并发单元,其调度由程序控制,而非操作系统内核。

关键词:

  1. 协作式:协程自愿、显式地让出执行权(yield),而不是被系统强行中断。
  2. 用户态:创建、调度、切换都在用户程序中完成,不涉及操作系统内核,开销极低(通常只是函数调用的级别)。
  3. 可挂起与恢复:这是协程最核心的能力。它能在任意点暂停,把执行现场(局部变量、程序计数器等)保存下来,之后可以原样恢复。

核心特点(与线程对比)

特性线程协程
所属层面操作系统内核提供和管理用户级,由运行时库或语言本身管理
调度方式抢占式。由系统内核的调度器决定何时切换,不可预测。协作式。协程自己主动让出控制权,程序员可预测切换点。
开销。需要陷入内核,进行上下文切换(保存寄存器、内存映射等)。极小。通常只是操作几个寄存器和栈帧,在用户态完成。
并发数量有限。受限于内核资源,创建数千个线程会导致系统性能急剧下降。极高。可轻松创建数万甚至数十万个协程(如Go语言)。
共享与同步需要复杂的锁、信号量等机制来同步共享内存访问,容易出错。通常单线程内运行,无需锁。通过通道(Channel)等高级抽象安全通信。
适用场景计算密集型、多核CPU并行。I/O密集型、高并发服务(网络服务器、爬虫等)。

为什么协程如此有用?(解决什么问题?)

主要解决 “高并发”“异步I/O” 的痛点。

传统阻塞I/O模型的问题: 一个线程处理一个网络连接,当等待数据时(比如从数据库读取),线程被操作系统挂起,白白占用内存和切换开销。连接数一多(如C10K问题),线程数暴涨,系统不堪重负。

使用协程的方案(以Go为例):

  1. 为每个连接创建一个协程。
  2. 当协程需要等待网络I/O时,它主动让出yield)执行权,告诉调度器:“我去等数据了,你去服务其他就绪的协程吧”。
  3. I/O事件就绪后(数据到了),调度器会恢复这个协程继续执行。
  4. 从程序员角度看,代码是顺序的、同步的(data := socket.read()),但底层却是非阻塞、异步的。这极大简化了并发编程。

典型实现与语言支持

  • Go语言:协程是其核心特性,称为 Goroutine。语言内置强大的调度器,使用关键字 go 即可启动。是协程最成功的应用范例之一。
  • Python:通过 asyncio 库和 async/await 关键字支持。
  • JavaScript:虽然本身是单线程事件循环,但 async/await 语法糖提供了类似协程的同步编程体验来处理Promise。

总结

协程的本质是一个可以暂停和恢复执行的函数,是用户态的、协作式的超轻量级线程。 它的最大价值在于:用同步的代码风格,写出高性能的异步程序,完美契合现代高并发、高I/O密度的网络服务需求,同时避免了多线程编程的复杂性和高开销。

技术文档集合