lua5.4 coroutine的通俗理解

官方文档中的解释

针对目前的lua5.4,官方api中对coroutine的解释如下

函数名 参数 返回值 作用
coroutine.create(f) function thread 创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回这个新协程,它是一个类型为 "thread" 的对象。
(对应函数:luaB_cocreate)
coroutine.isyieldable(co) thread boolean 如果协程 co 可以让出,则返回真。co 默认为正在运行的协程。
coroutine.close(co) thread boolean noerror
any errorobject
关闭协程 co,并关闭它所有等待 to-be-closed 的变量,并将协程状态设为 dead
coroutine.resume(co, val1, ...) thread
any
boolean,any 开始或继续协程 co 的运行。
(对应函数:luaB_coresume)
coroutine.status(co) thread string:
"running"正在运行;
"suspended"挂起或是还没有开始运行;
"normal"是活动的,但并不在运行;
"dead"行完主体函数或因错误停止。
以字符串形式返回协程 co 的状态。
coroutine.wrap(f) function fun(...):... 创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回一个函数, 每次调用该函数都会延续该协程。
coroutine.running() void thread,boolean 返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。
coroutine.yield(...) ... ... 挂起正在调用的协程的执行。 (对应函数:luaB_yield)

create最简单的写法就是

mythread = coroutine.create(function()
            print("hello wzh")
        end)

lua中的create是通过luaB_cocreate()来创建的

//lcorolib.c 95行
static int luaB_cocreate (lua_State *L) {
  lua_State *NL;//创建一个新的协程栈
  luaL_checktype(L, 1, LUA_TFUNCTION);//检查传入的参数是否为函数,如果不是函数就会停止整个lua虚拟机的执行
  NL = lua_newthread(L);	/* new一个新协程 */
  lua_pushvalue(L, 1);  /* move function to top */
  lua_xmove(L, NL, 1);  /* move function from L to NL */
  return 1;
}

create第一件事

主要做三件事,首先就是创建一个新的协程栈,协程栈主要包含

//lstate.h 303行
struct lua_State {
  CommonHeader;
  lu_byte status; //当前状态
  lu_byte allowhook; //是否允许hook
  unsigned short nci;  /* number of items in 'ci' list */
  StkId top;  /* first free slot in the stack */
  global_State *l_G;
  CallInfo *ci;  /* call info for current function */
  StkId stack_last;  /* end of stack (last element + 1) */
  StkId stack;  /* stack base */
  UpVal *openupval;  /* list of open upvalues in this stack */
  StkId tbclist;  /* list of to-be-closed variables */
  GCObject *gclist;
  struct lua_State *twups;  /* list of threads with open upvalues */
  struct lua_longjmp *errorJmp;  /* current error recover point */
  CallInfo base_ci;  /* CallInfo for first level (C calling Lua) */
  volatile lua_Hook hook;
  ptrdiff_t errfunc;  /* current error handling function (stack index) */
  l_uint32 nCcalls;  /* number of nested (non-yieldable | C)  calls */
  int oldpc;  /* last pc traced */
  int basehookcount;
  int hookcount;
  volatile l_signalT hookmask;
};

CommonHeader是一个数据头,所有的 GCObject 都有这个相同的数据头,这个数据头是一个宏,

#define CommonHeader    GCObject *next; lu_byte tt; lu_byte marked

所有的 GCObject 都用一个单向链表串了起来。每个对象都以 tt 来识别其类型。marked 域用于标记清除的工作(也就是gc操作,gc操作两元色到三元色的演进,在我看来清楚了gc和协程就lua就基本掌握的差不多了)。

lu_byte status;
/* thread status 有六种*/
#define LUA_OK		0
#define LUA_YIELD	1
#define LUA_ERRRUN	2
#define LUA_ERRSYNTAX	3
#define LUA_ERRMEM	4
#define LUA_ERRERR	5

allowhook是否允许hook的意思,在源码中,暂时只找到两个数值0和1,目前对hook的理解就是类似中断的一种机制

allowhook = 0;  /* cannot call hooks inside a hook */
allowhook = 0;  /* stop debug hooks during GC metamethod */
allowhook = 1; //允许hook

nci指的是ci列表中的条目数,ci指的是?

unsigned short nci;  /* number of items in 'ci' list */

堆栈中的第一个空闲槽

StkId top;  /* first free slot in the stack */

StkId在lobject.h中有定义

/*
** 标记的值。 这是Lua中值的基本表示形式:实际值以及带有其类型的标记。
*/

#define TValuefields	Value value_; lu_byte tt_

typedef struct TValue {
  TValuefields;
} TValue;

/*
** Lua堆栈中的条目。 字段“ tbclist”构成了此堆栈中所有活动的将要关闭的变量的列表。 
** 当两个tbc变量之间的距离不适合无符号短型时,将使用虚拟条目。 
** 它们用delta == 0表示,其实际delta始终是该字段中适合的最大值。
*/
typedef union StackValue {
  TValue val;
  struct {
    TValuefields;
    unsigned short delta;
  } tbclist;
} StackValue;


/* 第一个自由索引堆栈中的元素槽 */
typedef StackValue *StkId;

针对上述的Value其实是一个联合体,这个是lua中最为特别的联合体,被称为lua数据原子,用来完成绝大部分数据存储,第一种为可gc的对象,下面的四种为不需要gc

typedef union Value {
  struct GCObject *gc;    /* 可收集的对象 */
  void *p;         /* light userdata */
  lua_CFunction f; /* light C functions 6*/
  lua_Integer i;   /* integer numbers */
  lua_Number n;    /* float numbers */
} Value;

从源码看的话lua中的数据类型一共有10种,第十种是啥有点看不懂,等遇到了就知道了,不过这种基本数据类型的设计有点迷,

#define LUA_TNONE		(-1) //判断这个变量是否等于为空使用的,这个是Lua内部使用
#define LUA_TNIL		0 //全局变量没被赋值默认为nil,删除变量就赋值为 nil
#define LUA_TBOOLEAN		1 //布尔值
#define LUA_TLIGHTUSERDATA	2 //自定义类型,需要自己管理分配和回收,相关函数lua_pushlightuserdata()
#define LUA_TNUMBER		3 // 实数
#define LUA_TSTRING		4 //字符串
#define LUA_TTABLE		5 //数组,表
#define LUA_TFUNCTION		6 //函数
#define LUA_TUSERDATA		7 //自定义类型,是由LUA的GC机制进行回收,相关函数lua_newuserdata()
#define LUA_TTHREAD		8 //线程协程,相关函数lua_newthread,lua_newstate
#define LUA_NUMTYPES		9 //

create第二件事

就是将CallInfo操作栈上的协程回调函数,移动到L->top数据栈顶部

lua_pushvalue(L, 1);

create第三件事

就是将拷贝回调函数到协程的数据栈上

lua_xmove(L, NL, 1);

调用create后的协程是suspended状态的

原文地址:https://www.cnblogs.com/still-smile/p/14819470.html