前段时间研读云风的coroutine库,为了加深印象,做个简单的笔记。不愧是大神,云风只用200行的C代码就实现了一个最简单的协程,代码风格精简,非常适合用来理解协程和用来提升编码能力。
协程是用同步的写法达到异步的性能。其基本原理是在IO等待时切换出去,在适当的时刻切换回来,最大程度利用CPU。协程可以理解为一个用户级的线程,一个线程里跑多个协程。并且,不管协程数量多少,都是串行运行的,就是说不存在同一时刻属于一个线程的不同协程同时运行。因此避免了多线程编程可能导致的同步问题。
协程的行为有点像函数调用,但也有不同,对于函数调用来说,假如函数A调用函数B,则必须等待函数B执行完毕后才能重新返回A,但对于协程来说,如果再协程A中切换到协程B,协程B可以选择在某个点重新回到A的执行流,同时允许在某个时刻重新从A回到B之前运行到的那个点。这在函数中是不可能实现的,因为函数只能一路走到底。
既然允许协程中途中切换以及后期从新从切换点进入继续执行,说明必须有数据结构保存每个协程的上下文信息。云风携程库运用Linux中的ucontext实现协程见切换,而Linux包含以下几个系统函数对ucontext_t进行初始化、设置,以及基于ucontext_t切换协程:
getcontext() : 获取当前context
setcontext() : 切换到指定context
makecontext() : 设置函数指针和堆栈到对应context保存的sp和pc寄存器中,调用之前要先调用 getcontext()
swapcontext() : 保存当前context,并且切换到指定context
源码设计的数据结构有如下两个,schedule调度器管理协程,包括保存和切换协程,coroutine协程保存自身的上下文信息、主体函数和栈信息等。
struct schedule { char stack[STACK_SIZE]; //栈空间 ucontext_t main; //当前上下文 int nco; //协程数 int cap; //协程容量 int running; //是否正在运行 struct coroutine **co; //协程数组 }; struct coroutine { coroutine_func func; //协程运行主体函数 void *ud; //func的参数 ucontext_t ctx; //该协程的上下文信息 struct schedule *sch; //对应的调度器 ptrdiff_t cap; //协程大小 ptrdiff_t size; //协程实际大小 int status; //运行状态 char *stack; //栈 };
https://blog.csdn.net/u011228889/article/details/79759834
原文:https://www.cnblogs.com/evenleee/p/11425758.html