协程,简单来说就是新创建一个协助程序(co = coroutine.create(func)),然后需要手动去启动它(coroutine.resume(co)),在它最终退出之前,它有可能暂停多次返回阶段性的结果(coroutine.yield(co)),每一次暂停之后都必须手动去恢复它(coroutine.resume(co))。

协程在lua源文件中对应lcorolib.c,数组co_funcs中定义了c暴露给lua的接口。从上面的描述看和c函数调用有点相似,只不过c函数只有一个出口,所以不可能返回多次。题外话,为什么c函数只有一个出口?我自己粗浅的理解是因为c函数的所有信息都放在栈上,而c语言没有提供原生的保存/恢复栈空间的支持,所以没有中途退出后还能生新进入这个概念。实际上,协程和系统级别的进程切换更像一点,都是保存堆栈,然后恢复。我想最大的不同就是协程知道接下来的控制权在哪里,而进程不知道。根本上它们想实现的功能就不一样吧。

好了,那协程实现的要点就是堆栈的保存与恢复了。当然,这里的堆栈不是进程本身的堆栈,而是lua的soft stack。从代码上来说吧:

  static int luaB_cocreate (lua_State *L) {
    lua_State *NL;
    luaL_checktype(L, , LUA_TFUNCTION);
    NL = lua_newthread(L);
    lua_pushvalue(L, );  /* move function to top */
    lua_xmove(L, NL, );  /* move function from L to NL */
    ;
  }

其中NL就是新创建的协程的栈,以后所有的保存/恢复都是针对这个栈。lua_State这个结构体里对协程实现最重要的是CallInfo *ci,CallInfo的定义如下:

  /*
 67 ** information about a call
 68 */
  typedef struct CallInfo {
    StkId func;  /* function index in the stack */
    StkId top;  /* top for this function */
    struct CallInfo *previous, *next;  /* dynamic call link */
    short nresults;  /* expected number of results from this function */
    lu_byte callstatus;
    ptrdiff_t extra;
    union {
      struct {  /* only for Lua functions */
        StkId base;  /* base for this function */
        const Instruction *savedpc;
      } l;
      struct {  /* only for C functions */
        int ctx;  /* context info. in case of yields */
        lua_CFunction k;  /* continuation in case of yields */
        ptrdiff_t old_errfunc;
        lu_byte old_allowhook;
        lu_byte status;
      } c;
    } u;
  } CallInfo;

其中func指向当前调用的函数在栈上的位置,而savedpc就是保存的指令执行位置(先无视union里的c),根据这两个值就能恢复函数的执行点。然而在yield的时候真正负责保存函数位置的是extra(保存func与栈顶的相对位置),在resume时func会根据extra来恢复,有没有这个需要我是表示怀疑的,因为就算resume传递的参数导致栈realloc,使func失效,但在luaD_reallocstack内会调用correctstack将调用链上所有的func重新设置为正确的值,所以这里是不是多余的呢?

在lua 5.2中调用路径包含c函数的时候也能够进行yield,只不过不甚好看。由于c函数不能保存堆栈,所以lua的策略是直接放弃当前c函数的栈幀,而让调用者本身提供一个continuation,当resume时调用上面被无视的uion里的c.k。没用过,所以也不深入考究了。

lua协程实现简析的更多相关文章

  1. Lua 协程coroutine

    协程和一般多线程的区别是,一般多线程由系统决定该哪个线程执行,是抢占式的,而协程是由每个线程自己决定自己什么时候不执行,并把执行权主动交给下一个线程. 协程是用户空间线程,操作系统其存在一无所知,所以 ...

  2. [转]-Lua协程的实现

    协程是个很好的东西,它能做的事情与线程相似,区别在于:协程是使用者可控的,有API给使用者来暂停和继续执行,而线程由操作系统内核控制:另 外,协程也更加轻量级.这样,在遇到某些可能阻塞的操作时,可以使 ...

  3. LUA 协程

    LUA协程和C#协程非常相似,功能与用法更强大.基础用法: coco = coroutine.create(function (a,b) print("resume args:". ...

  4. lua 协程的理解

    参考链接: http://www.cnblogs.com/zrtqsk/p/4374360.html 对例子的自我理解: -- 协程的理解 -- co 是协程的内容,类似函数内容, 通过yield 将 ...

  5. Lua协程-测试3

    print("Lua 协程测试3") -- 实现消费者-生产者关系(生产一个就消费一个) count = -- 生产总数 -- 生产者 local newProductorCo = ...

  6. Lua协程-测试2

    print("Lua 协程测试2") function testFun(n) print("into foo,n = "..n) * n) -- 挂起co协程 ...

  7. lua协程实现

    协程是个很好的东西,它能做的事情与线程相似,区别在于:协程是使用者可控的,有API给使用者来暂停和继续执行,而线程由操作系统内核控制:另外,协程也更加轻量级.这样,在遇到某些可能阻塞的操作时,可以使用 ...

  8. 大富翁开发日记:一、使用巨型lua协程

    一个大胆的尝试:使用巨型lua协程来表示整个“一局”流程. lua协程是一个很另类的功能,有并发的影子但又不是真的并发,所以真正拿它来做大功能框架的范例不多,通常用于一些小型trick式设计.但这次我 ...

  9. lua协程并发下载简单测试

    下载8个1m大小文件,测试五次分别耗时12.038s,10.316s,8.955s,11.275s,9.499s(lua代码实现如下) require "socket" --hos ...

随机推荐

  1. 你真的了解load方法么?(转载)

    本文授权转载,作者:左书祺(关注仓库,及时获得更新:iOS-Source-Code-Analyze) 因为 ObjC 的 runtime 只能在 Mac OS 下才能编译,所以文章中的代码都是在 Ma ...

  2. 一款好用且免费的语句分析工具Plan Explorer

    在调优过程中的查询语句优化阶段,分析语句的执行计划是必经之路,一款好的执行计划分析工具确实可以帮助我们事半功倍 小贴士:Plan Explorer是将Plan Explorer 专业版与免费版整合在一 ...

  3. Contains Duplicate

    Given an array of integers, find if the array contains any duplicates. Your function should return t ...

  4. 尝试自己建立以alpine 为基础的docker基础镜像和组件镜像

    安装ubuntu14.04 然后 #获取root权限 sudo su #安装docker apt-get install docker #准备基础镜像 docker pull alpine docke ...

  5. LeetCode Reverse Linked List II 反置链表2

    题意:将指定的一段位置[m,n]的链表反置,返回链表头. 思路:主要麻烦在链表头,如果要从链表头就开始,比较特殊. 目前用DFS实现,先找到m-1的位置,再找到n+1的位置,中间这段就是否要反置的,交 ...

  6. firebug调试js时提示调试器未激活处理办法

    firebug是web开发中最常用的分析调试软件,不过我今天使用在调试百度在线编辑器UEditor时一直提示调试器未激活. 从使用经验来看不应该啊,我都下了断点了为什么会提示调试器未激活呢!多次载入网 ...

  7. django 常用命令

    django 常用命令,备忘: django-admin.py startproject test 创建一个项目名叫test的项目 python manage.py startapp app 创建一个 ...

  8. mysql命令行操作 添加字段,修改字段

    alter table  t_apply   change column     remarks(原)    apply_remarks(目标)   varchar(100) default '' c ...

  9. linux系统连接的概念及删除原理

    硬连接:ln 源文件 目标文件 软连接:ln -s 源文件 目标文件 (目标文件不能事先存在) 硬连接是通过索引节点inode来进行连接. 在Linux文件系统中,多个文件名指向同一个索引节点,硬连接 ...

  10. python全栈开发-Day10 装饰器(闭合函数的应用场)

    一. 装饰器 装饰器就是闭包函数的一种应用场景 什么是闭包函数?我们再来回忆一下: 闭包函数: 定义在函数内部的函数,并且该函数包含对外部函数作用域(强调:对全局作用域名字的引用不算闭包)名字的引用, ...