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;
}