1.lua中的协程
thread type
用于创建协程(coroutines),跟OS的thread不是一个概念
CoroutinesPython则不同,Python的线程就是OS原生线程;不过stackless和pypy的微线程跟这个概念差不多
又名collaborative multithreading(协作多线程)
每个协程是一个独立的线程(lua thread, not os thread),当前协程只有在显式调用yield时才会挂起
coroutine.create(..)创建协程,参数:协程要执行的main函数;返回值:thread对象,指向协程
coroutine.resume(..)启动/恢复协程,第一个参数是thread对象
启动时其他参数传递给协程对应的main函数
恢复时其他参数赋值给中断时yield赋值的对象
coroutine.yield(..)挂起协程,导致coroutine.resume()立即返回,返回值:true+yield的参数;
下次resume时从这个点恢复执行
The next time you resume the same coroutine, it continues its execution from the point where it yielded, with the call to coroutine.yield returning any extra arguments passed to coroutine.resume.
coroutine.wrap(..)创建协程,参数:协程要执行的main函数;返回值:function,调用function则等效于coroutine.resume(..),参数被传递给协程main函数
跟cotoutine.resume(..)的主要区别是不捕获错误,错误会向上传播,返回值没有第一个boolean.
其实说白了就是对main函数做了一个包装,不用显示的调用resume,可以差不多像原函数一样使用
协程结束:
1.协程执行的函数终结,这种情况coroutine.resume()返回true+协程函数返回值
2.协程执行的函数出现无保护的错误,这种情况coroutine.resume()返回false+错误信息
EXAMPLE:
[dongsong@localhost skynet]$ lua Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio > =coroutine table: 0x113f600 > for k,v in pairs(coroutine) do print(k,v) end resume function: 0x113f830 yield function: 0x113f9b0 status function: 0x113f8f0 wrap function: 0x113f950 create function: 0x113e2e0 running function: 0x113f890
[dongsong@localhost lua-study]$ cat coroutine.lua function foo (a) print("foo", a) return coroutine.yield(2*a) --resume returned: true 4 end co = coroutine.create(function (a,b) print("co-body", a, b) local r = foo(a+1) print("co-body", r) local r, s = coroutine.yield(a+b, a-b) --resume returned: true 11 -9 print("co-body", r, s) return b, "end" --resume returned: true 10 end end) print("main", coroutine.resume(co, 1, 10)) --main true 4 print("main", coroutine.resume(co, "r")) --main true 11 -9 print("main", coroutine.resume(co, "x", "y")) --main true 10 end print("main", coroutine.resume(co, "x", "y")) --main false cannot resume dead coroutine [dongsong@localhost lua-study]$ [dongsong@localhost lua-study]$ lua coroutine.lua co-body 1 10 foo 2 main true 4 co-body r main true 11 -9 co-body x y main true 10 end main false cannot resume dead coroutine
2.skynet对协程的改造过程:说白了,把lua默认的coroutine.resume和coroutine.yield作为upvalue打进了DIY的lresume和lyield,并把协程启动时间和累计运行时间作为upvalue打进lstart/lstop/lresume/lyield
int luaopen_profile(lua_State *L) { luaL_checkversion(L); luaL_Reg l[] = { { "start", lstart }, { "stop", lstop }, { "resume", lresume }, { "yield", lyield }, { NULL, NULL }, }; luaL_newlibtable(L,l); lua_newtable(L); // table thread->start time lua_newtable(L); // table thread->total time /* * table -> total time * table -> start time * table -> libtable */ lua_newtable(L); // weak table lua_pushliteral(L, "kv"); lua_setfield(L, -2, "__mode"); /* * table -> weak table * table -> total time * table -> start time * table -> libtable */ lua_pushvalue(L, -1); lua_setmetatable(L, -3); lua_setmetatable(L, -3); /* * table -> total time with weak metatable * table -> start time with weak metatable * table -> libtable */ lua_pushnil(L); luaL_setfuncs(L,l,3); /* * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop/lresume/lyield: table->starttime, table->totaltime, nil */ int libtable = lua_gettop(L); //libtable = 1 lua_getglobal(L, "coroutine"); //获取lua默认的coroutine库 lua_getfield(L, -1, "resume"); //获取lua默认的coroutine.resume函数 lua_CFunction co_resume = lua_tocfunction(L, -1); //co_resume=lua默认的coroutine.resume函数 if (co_resume == NULL) return luaL_error(L, "Can't get coroutine.resume"); lua_pop(L,1); //把lua默认的coroutine.resume从stack中弹出去 /* * coroutine (lua coroutine table) * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop/lresume/lyield: table->starttime, table->totaltime, nil */ lua_getfield(L, libtable, "resume"); /* * cfunction lresume * coroutine (lua coroutine table) * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop/lresume/lyield: table->starttime, table->totaltime, nil */ lua_pushcfunction(L, co_resume); /* * cfunction co_resume (lua default coroutine.resume) * cfunction lresume * coroutine (lua coroutine table) * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop/lresume/lyield: table->starttime, table->totaltime, nil */ lua_setupvalue(L, -2, 3); /* * cfunction lresume with upvalues: table->starttime, table->totaltime, co_resume (coroutine.resume) * coroutine (lua coroutine table) * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop/lyield: table->starttime, table->totaltime, nil * upvalues of lresume: table->starttime, table->totaltime, co_resume (coroutine.resume) */ lua_pop(L,1); /* * coroutine (lua coroutine table) * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop/lyield: table->starttime, table->totaltime, nil * upvalues of lresume: table->starttime, table->totaltime, co_resume (coroutine.resume) */ lua_getfield(L, -1, "yield"); lua_CFunction co_yield = lua_tocfunction(L, -1); //co_yield=coroutine.yield if (co_yield == NULL) return luaL_error(L, "Can't get coroutine.yield"); lua_pop(L,1); /* * coroutine (lua coroutine table) * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop/lyield: table->starttime, table->totaltime, nil * upvalues of lresume: table->starttime, table->totaltime, co_resume (coroutine.resume) */ lua_getfield(L, libtable, "yield"); lua_pushcfunction(L, co_yield); lua_setupvalue(L, -2, 3); /* * cfunction lyield with upvalues: table->starttime, table->totaltime, co_yield (coroutine.yield) * coroutine (lua coroutine table) * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop: table->starttime, table->totaltime, nil * upvalues of lresume: table->starttime, table->totaltime, co_resume (coroutine.resume) * upvalues of lyield: table->starttime, table->totaltime, co_yield (coroutine.yield) */ lua_settop(L, libtable); /* * table -> libtable: {start=lstart, stop=lstop, resume=lresume, yield=lyield}; * upvalues of lstart/lstop: table->starttime, table->totaltime, nil * upvalues of lresume: table->starttime, table->totaltime, co_resume (coroutine.resume) * upvalues of lyield: table->starttime, table->totaltime, co_yield (coroutine.yield) */ return 1; }