coroutine协程

如果你接触过lua这种小巧的脚本语言,你就会经常接触到一个叫做协程的神奇概念。大多数脚本语言都有对协程不同程度的支持。但是大多编译语言,如C/C++,根本就不知道这样的东西存在。当然也很多人研究如何在编译语言实现协程的实现,轮子一个又一个的被发明。酷壳 这篇文章《一个“蝇量级” C 语言协程库》说的很详细,但对于文中介绍的协程库protothread,很难看的懂。风云大哥在搜索无满意结果后也重新发明轮子,实现自己版本的一个协程库《C 的 coroutine 库》, 风云版本的coroutine是常规的根据getcontext/swapcontext的非常传统的方法,没有用到其他什么奇淫技巧。同时他接口几乎和lua协程的接口一样,比较容易看的懂,但是貌似和CPU构架相关,下面详细说。这篇文章就是主要写看风云的代码的,代码注释放到github上面coroutine,当然因为代码本身很精练和简单,注释也就非常少的。关于协程,可参考百度百科:协程

  开始之前,看看unix/linux下面的context系列函数风云大哥说,windows下面可以用纤程实现,以前看《windows核心编程》的时候,了解过这个概念,但是实际上编程运用的只限于进程/线程,没有用到过纤程这高级货,现在接触windows api也相对少,所以这个就不详细展开)。context系列函数大约就以下函数:

    #include <ucontext.h>        

    int getcontext(ucontext_t *ucp);
    int setcontext(const ucontext_t *ucp);        

    void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
    int swapcontext(ucontext_t *oucp, ucontext_t *ucp);

  根据linux手册页,getcontext返回的是当前执行进程的上下文信息,包括信号掩码,执行栈,寄存器等信息。比较值得注意的是makecontext,该函数除了传递ucp外,还传递一个通用的函数指针func和一个argc,传递给func的函数原型并不一定是void(*)()类型的,参数的个数由argc确定。当调用makecontext成功后,如果对这个上下文进行切换(swapcontext)或用这个上下文设置(setcontext),内核就会调用该func函数。协程的实现就是用到这个重要的原理。linux手册页有个详细的例子介绍makecontext怎么用的,请参考之。

  我们来看风云大哥的协程库:

    // 协程调度大小
    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;      // 用户数据
        ucontext_t ctx; // 保存的协程上下文状态
        struct schedule * sch; // 保存struct schedule指针
        ptrdiff_t cap; // 上下文切换时保存的栈的容量
        ptrdiff_t size; // 上下文切换时保存的栈的大小 size <= cap
        int status; // 协程状态
        char *stack; // 保存的协程栈大小
    };

  定义的结构体够简洁的,除了struct coroutine里面的capsize,感觉作用不大。上下文信息是在coroutine_resume里面设置的,这里将栈设置为自己定义的区域,大小也限定了,还设置了下个上下文切换的地址(在swapcontext里为他赋初始值):

    getcontext(&C->ctx);
    C->ctx.uc_stack.ss_sp = S->stack; // 设置栈
    C->ctx.uc_stack.ss_size = STACK_SIZE; // 设置栈大小
    C->ctx.uc_link = &S->main; // 下个切换的上下文状态,由swapcontext设置其值
    S->running = id;
    C->status = COROUTINE_RUNNING;
    uintptr_t ptr = (uintptr_t)S;
    makecontext(&C->ctx, (void (*)(void)) mainfunc, 2, (uint32_t)ptr, (uint32_t)(ptr>>32));
    swapcontext(&S->main, &C->ctx);

  风云大哥的协程库有点不好的地方就是这个协程库或许是与CPU的构架相关的,做成与CPU无关应该不难。看保存上下文信息的代码:

    static void
    _save_stack(struct coroutine *C, char *top) {
        char dummy = 0;
        //这里做了一个特定的假设
        // 栈由高地址向低地址方向增长,这种程序布局的CPU一般是X86构架的
        // 所以这个库时与CPU结构相关的。
        assert(top - &dummy <= STACK_SIZE); // stack大小
        if (C->cap < top - &dummy) {
            free(C->stack); // 首次C->stack为NULL,free(NULL)是OK的
            C->cap = top-&dummy;
            C->stack = malloc(C->cap);
        }
        C->size = top - &dummy;
        memcpy(C->stack, &dummy, C->size);
    }

  传递的参数top指向的是栈顶,栈顶与新定义的变量相减top - &dummy得到栈的大小。这里做了一个特定的假设,栈由高地址向低地址方向增长(这是非常典型的程序布局方式,虽然我不知道那种CPU不是用这种布局,但是觉得肯定有有低地址向高地址增长的),这种程序布局的CPU一般是X86构架的,所以这个库时与CPU结构相关的。

  总的来说,这个协程库写的非常精简。protothread这种高级货,暂时不看了。粗略的介绍完毕,如有错误,请批评指正。

  POST AT: http://luoguochun.cn/2014/08/21/coroutine/

coroutine协程

时间: 2024-10-13 06:47:07

coroutine协程的相关文章

[Unity-22] Coroutine协程浅析

1.概念解释 协程并非一个独立的线程.在Unity中.全部的语句都是在一个线程中运行的,也就是说.Unity是单线程的(详细的能够參见http://blog.csdn.net/alexander_xfl/article/details/41577625,这里面有对单线程的粗略解说). 那么协程究竟是什么呢?     官方的定义是这种: A coroutine is a function that is executed partially and, presuming suitable cond

测试-Coroutine协程能否受时间缩放影响

yield return new WaitForSeconds(1f); print("!!!" + Time.realtimeSinceStartup); yield return null; 答案是会受到 那么怎么忽略时间缩放?找了下官方没提供,也是一头雾水. 不过我不用协程,我有集成良好的计时器

Coroutine协程库:网络性能完爆Asio异步模型

在purecpp社区的github组织中有一个协程库:https://github.com/topcpporg/cpp_features 近日有用户找到我,想要了解一下coroutine库在网络方面的性能,于是选取已入选标准库的boost.asio网络库的异步模型做了一次对比测试,结果完爆asio异步模型,coroutine库网络性能是asio异步模型的2-14倍,使用8线程处理小包时,QPS可达670万/秒. 简要介绍一下测试流程: 1.TCP协议,C/S是同一台物理机的两个进程,建立1000

COROUTINE协程库:网络性能完爆ASIO异步模型(-O3测试)

在purecpp社区的github组织中有一个协程库:https://github.com/topcpporg/cpp_features 近日有用户找到我,想要了解一下coroutine库在网络方面的性能,于是选取已入选标准库的boost.asio网络库的异步模型做横向对比. 在小包和利用多核方面,coroutine库的网络性能完爆asio异步模型,8线程处理小包时差距可达十几倍. 在大包+单线程的情况,coroutine库的网络比asio异步模型高的不是很多,在一些性能比较差的PC机上,甚至出

Unity协程(Coroutine)原理深入剖析(转载)

记得去年6月份刚开始实习的时候,当时要我写网络层的结构,用到了协程,当时有点懵,完全不知道Unity协程的执行机制是怎么样的,只是知道函数的返回值是IEnumerator类型,函数中使用yield return ,就可以通过StartCoroutine调用了.后来也是一直稀里糊涂地用,上网google些基本都是例子,很少能帮助深入理解Unity协程的原理的. 本文只是从Unity的角度去分析理解协程的内部运行原理,而不是从C#底层的语法实现来介绍(后续有需要再进行介绍),一共分为三部分: 线程(

Unity协程(Coroutine)原理深入剖析

尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 记得去年6月份刚开始实习的时候,当时要我写网络层的结构,用到了协程,当时有点懵,完全不知道Unity协程的执行机制是怎么样的,只是知道函数的返回值是IEnumerator类型,函数中使用yield return ,就可以通过StartCoroutine调用了.后来也是一直稀里糊涂地用,上网google些基本都是例子,很少能帮助深入理解Unity协程的原理的. 本文只是从Unity的角度去分析理解协程的内部运行原理

Lua 协程 coroutine

协程是协同程序的简称,顾名思义,就是协同工作的程序.协程拥有自己独立的桟.局部变量和PC计数器,同时又与其他协同程序共享全局变量和其他大部分东西: 协程与线程的主要区别在于,一个多线程程序可以同时运行几个线程(并发执行.抢占),而协同程序却需要彼此协作地运行,即一个多协程程序在任意时刻只能运行一个协程,并且正在执行的协程只会在其显式地要求挂起(suspend)时,它的执行才会暂停(无抢占.无并发). Lua中所有与协程相关的函数都在coroutine(一个table)中: 函数create用于创

【转】Unity协程(Coroutine)原理深入剖析

Unity协程(Coroutine)原理深入剖析 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 记得去年6月份刚开始实习的时候,当时要我写网络层的结构,用到了协程,当时有点懵,完全不知道Unity协程的执行机制是怎么样的,只是知道函数的返回值是IEnumerator类型,函数中使用yield return ,就可以通过StartCoroutine调用了.后来也是一直稀里糊涂地用,上网google些基本都是例子,很少能帮助深入理解Unit

Day29:协程

一.协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈.因此: 协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置. 1.1 yield与协程 import time """ 传统的生产者-消