加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Lua中的协同程序 coroutine

发布时间:2020-12-14 21:54:59 所属栏目:大数据 来源:网络整理
导读:int running = 1; int lua_finish(lua_State * L) { running = 0; printf("lua_finish calledn"); return 0; } int lua_sleep(lua_State *L) { printf("lua_sleep calledn"); //lua_pushnumber(L,10); return lua_yield(L,0); } int main() { lua_State* L
int running = 1;


int lua_finish(lua_State * L) {
running = 0;
printf("lua_finish calledn");
return 0;
}
int lua_sleep(lua_State *L) {
printf("lua_sleep calledn");
//lua_pushnumber(L,10);
return lua_yield(L,0);
}


int main() {
lua_State* L = luaL_newstate();
luaL_openlibs(L);


lua_register(L,"sleep",lua_sleep);
lua_register(L,"finish",lua_finish);


//luaL_dofile(L,"scripts/init.lua");


lua_State* cL = lua_newthread(L);
luaL_loadfile(cL,"loop.lua");


while (running) {
int status;
lua_pushnumber(cL,20);
status = lua_resume(cL,1);
if (status == LUA_YIELD) {
printf("loop yieldingn");
}
else {
running = 0; // you can't try to resume if it didn't yield
// catch any errors below
if (status == LUA_ERRRUN && lua_isstring(cL,-1)) {
printf("isstring: %sn",lua_tostring(cL,-1));
lua_pop(cL,-1);
}
}
}


//luaL_dofile(L,"scripts/end.lua");
lua_close(L);
return 0;

}


lua file


print("loop.lua")


local i = 0
while true do
? ? print("lua_loop iteration")
? ? local bret = sleep()
print(bret)


? ? i = i + 1
? ? if i == 4 then
? ? ? ? break
? ? end
end


finish()


 Lua中的协程和多线程很相似,每一个协程有自己的堆栈,自己的局部变量,可以通过yield-resume实现在协程间的切换。不同之处是:Lua协程是非抢占式的多线程,必须手动在不同的协程间切换,且同一时刻只能有一个协程在运行。并且Lua中的协程无法在外部将其停止,而且有可能导致程序阻塞。

?

协同程序(Coroutine):

  三个状态:suspended(挂起,协同刚创建完成时或者yield之后)、running(运行)、dead(函数走完后的状态,这时候不能再重新resume)。

  coroutine.create(arg):根据一个函数创建一个协同程序,参数为一个函数

  coroutine.resume(co):使协同从挂起变为运行(1)激活coroutine,也就是让协程函数开始运行;(2)唤醒yield,使挂起的协同接着上次的地方继续运行。该函数可以传入参数

  coroutine.status(co):查看协同状态

  coroutine.yield():使正在运行的协同挂起,可以传入参数

  resume函数的两种用途虽然都是使协同挂起,但还是有些许差异的,看下面这个例子:

coroutineFunc = function (a,b) 
    for i = 1,10 do
        print(i,a,b)
        coroutine.yield()
    end
end

co2 = coroutine.create(coroutineFunc)        --创建协同程序co2
coroutine.resume(co2,128)">100,128)">200)                 1 100 200 开启协同,传入参数用于初始化
coroutine.resume(co2)                         2 100 200 
500,128)">600)                 3 100 200 继续协同,传入参数无效

co3 = 创建协同程序co3
coroutine.resume(co3,128)">300,128)">400)                 1 300 400 开启协同,传入参数用于初始化
coroutine.resume(co3)                         2 300 400 
 3 300 400 

?  Lua中协同的强大能力,还在于通过resume-yield来交换数据:

  (1)resume把参数传给程序(相当于函数的参数调用);

  (2)数据由yield传递给resume;

  (3)resume的参数传递给yield;

  (4)协同代码结束时的返回值,也会传给resume

?  协同中的参数传递形势很灵活,一定要注意区分,在启动coroutine的时候,resume的参数是传给主程序的;在唤醒yield的时候,参数是传递给yield的。看下面这个例子:

co = coroutine.create(print("co",b,coroutine.yield()) end)
coroutine.resume(co,128)">2)        没输出结果,注意两个数字参数是传递给函数的
3,128)">4,5)        co 1 2 3 4 5,这里的两个数字参数由resume传递给yield 

  Lua的协同称为不对称协同(asymmetric coroutines),指“挂起一个正在执行的协同函数”与“使一个被挂起的协同再次执行的函数”是不同的,有些语言提供对称协同(symmetric coroutines),即使用同一个函数负责“执行与挂起间的状态切换”。

?  注意:resume运行在保护模式下,因此,如果协同程序内部存在错误,Lua并不会抛出错误,而是将错误返回给resume函数。

?  以下是我个人的一点理解:

  (1)resume可以理解为函数调用,并且可以传入参数,激活协同时,参数是传给程序的,唤醒yield时,参数是传递给yield的;

  (2)yield就相当于是一个特殊的return语句,只是它只是暂时性的返回(挂起),并且yield可以像return一样带有返回参数,这些参数是传递给resume的。

为了理解上面两句话的含义,我们来看一下如何利用Coroutine来解决生产者——消费者问题的简单实现:

produceFunc = function()
    while true do
        local value = io.read()
        produce: coroutine.yield(value)        返回生产的值
    end

consumer = function(p)
    local status,value = coroutine.resume(p);        唤醒生产者进行生产
        consume: end

消费者驱动的设计,也就是消费者需要产品时找生产者请求,生产者完成生产后提供给消费者
producer = coroutine.create(produceFunc)
consumer(producer)

这是一种消费者驱动的设计,我们可以看到resume操作的结果是等待一个yield的返回,这很像普通的函数调用,有木有。我们还可以在生产消费环节之间加入一个中间处理的环节(过滤器):

produceFunc = end

filteFunc = coroutine.resume(p);
        value = value *100            放大一百倍
        coroutine.yield(value)
    function(f,p)
    coroutine.resume(f,p);        coroutine.create(produceFunc)
filter = coroutine.create(filteFunc)
consumer(filter,producer)

  可以看到,我们在中间过滤器中将生产出的值放大了一百倍。

  通过这个例子应该很容易理解coroutine中如何利用resume-yield调用来进行值传递了,他们像“调用函数——返回值”一样的工作,也就是说resume像函数调用一样使用,yield像return语句一样使用。coroutine的灵活性也体现在这种通过resume-yield的值传递上。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读