首发于微信公众号:【码农在新加坡】,欢迎关注。
个人博客网站:一文看懂Go语言协程的设计与原理
背景
Go语言最大的特色就是从语言层面支持并发(Goroutine),Goroutine是Go中最基本的执行单元。事实上每一个Go程序至少有一个Goroutine:main Goroutine。Go 程序从 main 包的 main() 函数开始,在程序启动时,Go 程序就会为 main() 函数创建一个默认的 goroutine。
为了了解Go语言协程的设计,我们从历史设计出发,来看看最终Goroutine怎么一步一步到现在的设计的。
单进程时代
早期的操作系统每个程序就是一个进程,操作系统在一段时间只能运行一个进程,直到这个进程运行完,才能运行下一个进程,这个时期可以成为单进程时代——串行时代。
如图:进程之间串行执行,A、B、C 三个进程按顺序执行。
单进程时代的两个问题:
- 单一执行流程、计算机只能一个任务一个任务的处理。
- 进程阻塞所带来的CPU浪费时间是不可避免的(如进程A阻塞了,然后CPU是单进程没有任何的切换能力,但是需要等待进程A结束后才能执行下个进程)
遇到这种问题,我们怎么才能充分利用CPU呢?
多进程时代
后来操作系统就具有了最早的并发能力:多进程并发,当一个进程阻塞的时候,切换到另外等待执行的进程,这样就能尽量把CPU利用起来,CPU就不浪费了。
在多进程时代,有了时间片的概念,进程按照调度算法分时间片在 CPU 上执行,A、B、C 三个进程按照时间片并发执行。(调度算法)
这样做有两个优点:
- 对于单个核可以并发执行多个进程,应用场景更加丰富,
- 当某个进程 IO 阻塞时,也能保证 CPU 的利用率。
但是随着时代的发展,CPU 通过进程来进行调度的缺点也越发的明显。
进程切换需要:
- 切换页目录以使用新的地址空间
- 切换内核栈和硬件上下文
因为进程拥有太多资源,在创建、切换和销毁的时