LUA数据结构(二)
Lua数据结构
thread
Lua中,最主要的线程是协程,它和线程差不多,拥有独立的栈、局部变量和指令指针;和线程区别在于线程可以同时运行多个,而协程同一时刻只能有一个运行
Lua协程接口都放在table coroutine里面,主要包括以下几个:
coroutine.create,创建一个协程
coroutine.wrap
coroutine.resume,重启一个协程
coroutine.yield,挂起一个协程
coroutine.status,查看协程状态
coroutine.running,返回当前运行的协程
create和wrap
coroutine.create和coroutine.wrap都是用来创建协程的,区别在于:
(1)create接收一个方法为参数,这个方法即协程的主流程,返回值可以认为是一个coroutine对象,通过resume接口唤醒这个协程;
(2)wrap接收一个方法为参数,返回值是一个函数,每次调用相当于将协程拉起
coroutine.create用法示例:
function func_co1() local i=0; while true do print("co1",i) i=i+1 if i>10 then return end if i%3==0 then i=i+1 coroutine.resume(co2) end end end function func_co2() local i=0; while true do print("co2",i) i=i+1 if i>10 then return end if i%3==0 then i=i+1 coroutine.yield(co2) end end end co1 = coroutine.create(func_co1) co2 = coroutine.create(func_co2) coroutine.resume(co1)
本例中:
1)通过接口coroutine.create创建了协程co1和co2,并通过resume接口拉起;
2)create返回的co1和co2是thread类型;
3)Lua的协程中的局部变量(这里的local i)是协程的私有变量,不会互相影响;但是全局变量是所有协程公用的,使用时候需要注意其中的同步问题;
4)在Lua中,同一时刻只有一个协程运行,即在协程co1中拉起co2,则co1停止执行,将执行权限交给co2,直到co2将执行权限让出,co1才能继续执行。
coroutine.wrap用法示例:
function co_wrapfunc_1() for i=0,10 do print("co_wrap_1:",i) if i%3==0 then co_wrap_2("aaa","bbb","ccc") end end end function co_wrapfunc_2(...) for i=0,10 do print("co_wrap_2:",i) if i%3==0 then ret = select(2,...) print(ret) coroutine.yield(coroutine.running()) end end end co_wrap_1=coroutine.wrap(co_wrapfunc_1) co_wrap_2=coroutine.wrap(co_wrapfunc_2) co_wrap_1()
本例中:
1)coroutine.wrap返回值是一个function类型,每次唤醒这个协程通过类似函数调用的形式即可;
2)拉起协程类似函数调用的格式,支持参数传递,协程内也可以获取到这个参数,作为唤醒协程后执行的一些输入;
3)协程被重新拉起时,从上次挂起的位置后一行代码继续往后执行,本例中,对于协程co_wrap_2,重新拉起后,在yield下一句重新开始执行;对于协程co_wrap_1,在拉起协程2的后一句开始执行;
4)对于coroutine.wrap创建的协程,返回的不是协程的对象而是被封装的方法,因此在yield出CPU的时候需要通过coroutine.running来获取当前正在运行的协程。
Lua的协程状态
在介绍resume和yield接口之前,先介绍一下Lua协程的状态,Lua协程分为suspend、running、dead、normal状态,其中:
1)coroutine.create/coroutine.wrapper,协程创建之后处于suspend状态;
2)coroutine.resume,当协程挂起之后,调用resume接口,协程转入running状态;
3)当在协程内调用coroutine.resume,则主调协程进入normal状态,此时不是running状态无法挂起,不是挂起状态也无法resume;需要等被调用协程执行完毕才能返回原协程;
4)当在协程内调用coroutine.yield,则主调协程进入suspend状态;
5)协程内函数执行完毕,则协程进入dead状态。
yield和resume
Lua协程的normal状态
Lua中通过yield和resume进行协程执行权的让出和转移,需要注意的一点是Lua是非对称的协程模型,即在协程中可以拉起协程,但被拉起的协程和原协程之间关系是不对等的,不能被拉起协程的子协程拉起父协程的操作,如下示例:
function func_co1() local i=0 while true do print("co1",i) i=i+1 if i>10 then print("co1 return") return end if i%3==0 then i=i+1 coroutine.resume(co2) coroutine.yield(co1) end end end function func_co2() local i=0 while true do print("co2",i) i=i+1 if i>10 then print("co2 return") return end if i%3==0 then i=i+1 coroutine.resume(co1) coroutine.yield(co2) end end end co1 = coroutine.create(func_co1) co2 = coroutine.create(func_co2) coroutine.resume(co1)
此时协程最终会被卡住,更明显的如下:
co_wrap_1中拉起co_wrap_2之后,co_wrap_1事实上处于normal状态,此时在co_wrap_2中再去拉起co_wrap_1,则认为非挂起状态,协程不能被重新拉起而报错。
yield和resume的参数
如前所述,子协程通过yield将自己的执行权交还给父协程,有些场景下,父协程想拿到子协程的一部分执行结果作为后续执行的依据,yield函数提供了参数列表,将部分需要返给父协程的信息通过这个列表传递回去,如下:
function func_co1() local i=0 while true do print("co1",i) i=i+1 if i>10 then return end if i%3==0 then i=i+1 ret1,ret2,ret3=coroutine.resume(co2) print(ret1,ret2,ret3) end end end function func_co2() local i=0; j=0; while true do print("co2",i,j) i=i+1 if i>10 then return end if i%3==0 then i=i+1 coroutine.yield("aaaa","bbbbb") end end end co1 = coroutine.create(func_co1) co2 = coroutine.create(func_co2) coroutine.resume(co1)
在本例中:
1)协程co1通过resume接口拉起协程co2,协程co2执行完毕之后通过yield让出执行权,并且将执行的结果通过yield形参列表给出来
2)协程co1通过resume的返回值获取执行结果,其中第一个返回值为true/false,用来标识协程是否正常执行完毕,后面的参数用于获取子协程的返回值
3)需要注意的是,当协程已经处于dead状态的时候,不能再启动这个协程,否则会报错,并返回false
参考文件
https://segmentfault.com/a/1190000024528016