LUA数据结构(二)

ronald4个月前职场1100

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)

    此时协程最终会被卡住,更明显的如下:

image-20220112105243410.png

    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



参考文件

    构建Lua解释器Part11:Upvalue - 知乎 (zhihu.com)

    scope - In Lua, is there a difference between local functions declared with and without the "local" keyword? - Stack Overflow

    Lua 协同程序(coroutine) | 菜鸟教程 (runoob.com)

    深入理解lua的协程coroutine_papaya的博客-CSDN博客_lua协程

    https://segmentfault.com/a/1190000024528016


相关文章

LUA数据结构(三)

Lua数据结构userdata    Lua官方的介绍:userdata是一种用户自定义数据,用于表示一种由应用程序或者C/C++语言库创建的类型,可以将任意C/C++类型的数据(通常是struct、指针)存储到Lua变量中调用。    在实际应用过程中,C/C++接口调用LuaL_newuserdata就会分配指定大...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。