一: lua协同程序
1 : 关于coroutine.resume(co, ...)和coroutine.yield(xxx)两个函数的参数和返回值
1.1: 如果coroutine.resume(co, ...)调用失败。print(coroutine.resume(co, ...)) 打印 false;如果是第一次调用resume,除了第一个参数以外,resume调用的其余参数值都视为给协同程序的主函数传递参数。比如coroutine.resume(co, 1,2,3)相当于给协同程序的主函数传递了1,2,3这三个int类型的参数值。
1.2:如果coroutine.resume(co, ...)调用成功。coroutine.resume(co, ...)则有可能出现两种返回值:请看以下
local co = coroutine.create(function (...)
print("arg: ", ...)
print("yield return: ", coroutine.yield("coroutine stop", ...))
print("restart coroutine:", ...)
return "coroutine end"
end)
print("resume 1: ", coroutine.resume(co, 1, 2, 3))
print("resume 2: ", coroutine.resume(co, 4, 5, 6,"restart"))
运行lua coro.lua的结果如下:
[root@localhost testLua]# lua coro.lua
arg: 1 2 3
resume 1: true coroutine stop 1 2 3
yield return: 4 5 6 restart
restart coroutine: 1 2 3
resume 2: true coroutine end
从coro.lua的运行结果可以看出来:
第一,coroutine.resume遇到协同程序主函数的coroutine.yield的时候,主函数处于挂起状态,resume的返回值是:true 后加 coroutine.yield的参数。
第二,coroutine.resume调用遇到协同程序主函数结束的时候,主函数运行结束。resume的返回值是:true后加主函数的return值。
从coro.lua的例子也可以看出来coroutine.yield()函数的运行机制:
第一:主函数第一次运行到coroutine.yield函数的时候,主函数挂起;coroutine.yield并不返回任何值,仅仅是存储下了resume传递的参数。
第二:coroutine.resume(co, 4, 5, 6,"restart")再次启动主函数的时候,主函数继续运行,coroutine.yield有返回值,返回值是本次resume传递的参数值。
第三:coroutine.resume(co, 4, 5, 6,"restart")再次启动主函数的时候,主函数接着往下运行,但是本次resume传递的参数值,并不影响主函数的后续执行结果。也就是说,主函数coroutine.yield往后的代码执行结果取决于第一次resume传递的参数。所以print("restart coroutine:", ...)的运行结果是:
restart coroutine: 1 2 3
2:关于c API lua_resume于Lua交互的栈结果,请看以下lua程序foo.lua和c++程序lua_resume_test.cpp
function Func2(value)
print("Func2 begin.")
coroutine.yield(5, value + 10,value+20,value+30)
print("Func2 ended.")
end
function Func1(param1)
print("Func1 begin")
Func2(param1 + 10)
print("Func1 ended.")
return 100,"game over"
end
#include
#include
#include
#include
using namespace std;
int main()
{
//创建主线程
lua_State *L = luaL_newstate();
//打开lua辅助库
luaL_openlibs(L);
lua_State *L1 = lua_newthread(L);
if (!L1)
{
return 0;
}
//L的栈顶是L1,thread类型的值
cout << "L Element Num:" << lua_gettop(L)<<" " << lua_type(L,1) << endl;
//在L1加载lua文件
luaL_dofile(L1,"foo.lua");
lua_getglobal(L1, "Func1");
lua_pushinteger(L1, 10);
cout << "L1 Element Num:" << lua_gettop(L1) << endl;
// 运行这个协同程序
// // 这里返回LUA_YIELD
int iRet = lua_resume(L1, 1);
cout << "LUA_YIELD = " << LUA_YIELD << endl;
cout << "iRet:" << iRet << endl;
// // 打印L1栈中元素的总数
cout << "Element Num:" << lua_gettop(L1) << endl;
// 打印yield返回的四个值
cout << "Value 1:" << lua_tointeger(L1, 1) << endl;
cout << "Value 2:" << lua_tointeger(L1, 2) << endl;
cout << "Value 3:" << lua_tointeger(L1, 3) << endl;
cout << "Value 4:" << lua_tointeger(L1, 4) << endl;
/// 再次启动协同程序
/// 这里返回0
iRet = lua_resume(L1, 0);
cout << "iRet:" << iRet << endl;
cout << "Element Num:" << lua_gettop(L1) << endl;
cout << "Value 1:" << lua_tointeger(L1, -2) << endl;
cout << "Value 1:" << lua_tostring(L1, -1) << endl;
//关闭主线程
lua_close(L);
return 0;
}
[root@localhost testLua]# g++ -g lua_resume_demo.cpp -ldl -llua
[root@localhost testLua]# ./a.out
L Element Num:1 8
L1 Element Num:2
Func1 begin
Func2 begin.
LUA_YIELD = 1
iRet:1
Element Num:4
Value 1:5
Value 2:30
Value 3:40
Value 4:50
Func2 ended.
Func1 ended.
iRet:0
Element Num:2
Value 1:100
Value 1:game over
上面的例子是C语言调用Lua代码,Lua可以自己挂起自己。lua_resume可以启动一个协同程序,它的用法就像lua_call一样。将待调用的函数压入栈中,并压入其参数,最后在调用lua_resume时传入参数的数量narg。这个行为与lua_pcall类似,但有3点不同。
当lua_resume返回LUA_YIELD时,线程的栈中只能看到交出控制权时所传递的那些值。调用lua_gettop则会返回这些值的数量。为了恢复一个挂起线程的执行,可以再次调用lua_resume。在这种调用中,Lua假设栈中所有的值都是由yield调用返回的,当然了,你也可以任意修改栈中的值。
3:关于C API lua_yield的使用:
3.1编写调用lua_yield API的c函数库lua_yield_demo.c:
#include
#include
#include
#include
static int IsSet(lua_State *L)
{
lua_getfield(L, LUA_ENVIRONINDEX, "leisure");
if (lua_isnil(L, -1))
{
printf("Not set\n");
return 0;
}
printf("leisure is seted\n");
return 1;
}
static int Func1(lua_State *L)
{
if (!IsSet(L))
{
lua_pushinteger(L, 1000);
printf("stack size:%d\n",lua_gettop(L));
printf("Begin yield,end return 1000 to lua programme\n");
return lua_yield(L, 1);
}
printf("Resumed again\n");
lua_getfield(L, LUA_ENVIRONINDEX, "leisure");
printf("stack size:%d\n",lua_gettop(L));
return 1;
}
static int Func2(lua_State *L)
{
printf("Func2 stack size:%d,stack value:%d\n",lua_gettop(L),lua_tointeger(L,1)
);
luaL_checkinteger(L, 1);
lua_pushvalue(L, 1);
printf("Func2 stack size:%d,stack value:%d\n",lua_gettop(L),lua_tointeger(L,1)
);
lua_setfield(L, LUA_ENVIRONINDEX, "leisure");
printf("Func2 stack size:%d,stack value:%d\n",lua_gettop(L),lua_tointeger(L,1)
);
return 0;
}
static luaL_Reg luayielddemo[] = {
{"IsSet", IsSet},
{"Func1", Func1},
{"Func2", Func2},
{NULL, NULL}
};
int luaopen_luayielddemo(lua_State* L)
{
///设置c模块环境表
lua_newtable(L);
printf("stack size:%d,%s,%s\n",lua_gettop(L),lua_tostring(L,1),lua_istable(L,2
)?"true":"false");
lua_replace(L,LUA_ENVIRONINDEX);
printf("stack size:%d,stack value:%s\n",lua_gettop(L),lua_tostring(L,1));
///注册c函数
const char* libName = "luayielddemo";
luaL_register(L,libName,luayielddemo);
return 1;
}
生成动态库文件:gcc -g lua_yield_demo.c -fPIC -shared -o luayielddemo.so
3.2编写调用动态库luayielddemo.so的lua程序 luayield.lua
local Module = require"luayielddemo"
local function1 = function (a,b,c)
local value
repeat
value = Module.Func1(a,b,c)
until value
return value
end
local thread1 = coroutine.create(function1)
print(coroutine.resume(thread1,1,2,3))
print(coroutine.status(thread1))
-- 设置C函数环境
Module.Func2(9999)
print(coroutine.resume(thread1,4,5,6))
3.3:运行luayield.lua程序
[root@localhost testLua]# lua luayield.lua
stack size:2,luayielddemo,true
stack size:1,stack value:luayielddemo
Not set
stack size:5
Begin yield,end return 1000 to lua programme
true 1000
suspended
Func2 stack size:1,stack value:9999
Func2 stack size:2,stack value:9999
Func2 stack size:1,stack value:9999
true 4
3.4:通过结果可以看出来,C代码调用lua_yield不能让c程序停止运行,但是lua_yield可以让它的lua调用者挂起。