从云风的coroutine库学习协程

协程又被称为微线程,不过其实这样的称呼无形中为理解协程增加了一点阻碍。协程本质上是在一个线程里面,因此不管协程数量多少,它们都是串行运行的,也就是说不存在同一时刻,属于同一个线程的不同协程同时在运行。因此它本身避免了所有多线程编程可能导致的同步问题。

协程的行为有点像函数调用,它和函数调用的不同在于,对于函数调用来说,假如A函数调用B函数,则必须等待B函数执行完毕之后程序运行流程才会重新走回A,但是对于协程来说,如果在协程A中切到协程B,协程B可以选择某个点重新回到A的执行流,同时允许在某个时刻重新从A回到B之前回到A的那个点,这在函数中是不可能实现的。因为函数只能一走到底。用knuth的话来说:

子程序就是协程的一种特例

既然允许协程中途切换,以及后期重新从切换点进入继续执行,说明必须有数据结构保存每个协程的上下文信息。这就是ucontext_t,而linux中包含以下几个系统函数对ucontext_t进行初始化,设置,以及基于ucontext_t进行协程间的切换:

  1. int getcontext(ucontext_t *);
  2. int setcontext(const ucontext_t *);
  3. void makecontext(ucontext_t , (void )(), int, …);
  4. int swapcontext(ucontext_t , const ucontext_t );

下面根据云风的精简协程库来说明如何用这些函数和ucontext_t来实现一个协程池。

coroutine的实现分析

coroutine里面主要是由协程管理结构schedule和以下几个核心函数组成 :

  1. coroutine_resume
  2. coroutine_yield

coroutine由用户协程和一个管理作用的协程组成。每次协程切换时,用户协程必须先切换到管理协程,然后管理协程再负责切换到其他的用户协程,不能直接从用户协程切换到其他用户协程。从以上两个函数来说,coroutine_resume就是从管理协程切换到用户协程的入口,coroutine_yield是从用户协程切换到管理协程的入口。

下面着重分析一下这两个函数:

void 
coroutine_resume(struct schedule * S, int id) {
    assert(S->running == -1);
    assert(id >=0 && id < S->cap);
    struct coroutine *C = S->co[id];
    if (C == NULL)
        return;
    int status = C->status;
    switch(status) {
    //第一次被切换到的用户协程处于COROUTINE_READY
    case COROUTINE_READY:
        getcontext(&C->ctx);
        C->ctx.uc_stack.ss_sp = S->stack;
        C->ctx.uc_stack.ss_size = STACK_SIZE;
        C->ctx.uc_link = &<
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值