本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Golang 协程是什么
线程
我们都知道,线程是进程中的执行体,拥有一个执行路口,以及从进程虚拟地址空间中分配的栈。包括用户栈和内核栈。 操作系统会记录线程控制信息,而线程获得CPU时间片以后才可以执行。CPU的栈指针、指令指针等寄存器,都要切换到对应的线程。
协程
如果线程自己又创建几个执行体,给他们各自指定执行入口,申请一些内存给它们用做执行栈,那么线程就可以按需调度这几个执行体了。为了实现这些执行体的切换,线程也需要记录它们的控制信息。包括ID、栈的位置、执行入口地址、执行现场等等。
线程可以选择一个执行体来执行,此时CPU中指令指针就会指向这个执行体的入口,栈基和栈指针寄存器也会指向线程给它分配的执行栈。
切换执行体
要切换执行体时,需要先保存当前执行体的执行现场,然后切换到另一个执行体。通过同样的方式,可以恢复到之前的执行体,这样就可以从上次中断的地方继续执行。
这些由线程创建的执行体就是所谓的协程。
因为用户程序不能操作内核空间,所以只能给协程分配用户栈,而操作系统对用户栈一无所知。所以协程又被称为用户态线程。
协程思想的关键
在创建协程时,都要指定执行入口,底层都会分配协程执行栈和控制信息,否则又该如何实现用户态调度?
而让出执行权时,也都要保存执行现场,不然如何能从中断处恢复执行?
所以协程思想的关键在于控制流的主动让出和恢复,每个协程拥有自己的执行栈,可以保存自己的执行现场。所以可以由用户程序,按需创建协程。
协程的主动让出和恢复执行
协程主动让出执行权时,会保存执行现场,然后切换到其它协程。
协程恢复执行时,会根据之前保存的执行现场,恢复到中断前的状态,继续执行。
这样,就通过协程实现了既轻量又灵活的、由用户态进行调度的多任务模型。
真正让协程大放异彩的是它在IO多路复用中的应用,二者的结合是一种炙手可热的高并发解决方案。