lua的协程

  lua协程的创建通常是通过coroutine.create(f),其中f就是协程的主体程序,它必须是一个函数。coroutine.create返回一个类型为thread(lua的8种内建类型之一)的变量。

---
--- Creates a new coroutine, with body `f`. `f` must be a Lua function. Returns
--- this new coroutine, an object with type `"thread"`.
---@param f fun():thread
---@return thread
function coroutine.create(f) end

协程的状态和状态切换

  协程有4种状态,分别是:

状态 说明
running 运行状态,协程主体函数正在执行时的状态
suspended 挂起状态,协程调用yeild,或者刚刚创建完成时的状态
normal 正常状态,协程已激活(resume),但是执行序列不在此协程中,通常是协程嵌套时
dead 死亡状态,协程主体函数执行完毕,或者主体函数执行异常,停止后的状态

  下面一个例子展示了协程除normal态的切换例子

#!/usr/bin/lua

local co

function routine()
    print("在协程主体函数执行时,查询到的协程状态:", coroutine.status(co))
    coroutine.yield()
    print("退出协程了")
end

function main()
    co = coroutine.create(routine)
    print("刚创建完协程时的状态:", coroutine.status(co))
    coroutine.resume(co)
    print("调用resume启动协程,yield后的状态:", coroutine.status(co))
    coroutine.resume(co)
    print("刚创建完协程时的状态:", coroutine.status(co))
end

main()

  在理解上面例子前,需要知道yieldresume的作用,类比linux下的调度,协程的resume,就相当于让指定的协程获得执行权,而yield就是协程主动让出执行权。

  上面的例子执行的结果是

刚创建完协程时的状态:	suspended
在协程主体函数执行时,查询到的协程状态:	running
调用resume启动协程,yield后的状态:	suspended
退出协程了
刚创建完协程时的状态:	dead

  这里我特意把控制协程运行相关的代码写在一个单独的main函数里面。为了方便理解,暂且把协程当成线程理解(它俩不是一回事,仅为了好理解),首先main未调用 coroutine.create(routine)时,该进程是单线程的,调用后,此时进程中包含了2个线程,主线程main和子线程routine。当调用coroutine.resume(co)时,主线程让出执行权,子线程开始执行(变成running态),子线程执行到coroutine.yield()后,它又会让出执行权(变成suspended态),回到交给它执行权的主线程中。当子线程的语句执行完毕,它会彻底让出执行权(变成dead态),永远无法再次resume,即使强制调用,也会返回错误`。

还剩余一个normal态未体现出来,下面这个简单的例子可以看到normal

#!/usr/bin/lua

local co1, co2

function routine1()
    print("第一级协程")
    print("在第一级协程查询到的第一级协程状态:", coroutine.status(co1))
    print("在第一级协程查询到的第二级协程状态:", coroutine.status(co2))
    coroutine.resume(co2)
    print("第一级协程退出了")
end

function routine2()
    print("第二级协程")
    print("在第二级协程查询到的第一级协程状态:", coroutine.status(co1))
    print("在第二级协程查询到的第二级协程状态:", coroutine.status(co2))
    print("第二级协程退出了")
end

function main()
    co1 = coroutine.create(routine1)
    co2 = coroutine.create(routine2)
    coroutine.resume(co1)
end

main()

执行结果

第一级协程
在第一级协程查询到的第一级协程状态:	running
在第一级协程查询到的第二级协程状态:	suspended
第二级协程
在第二级协程查询到的第一级协程状态:	normal
在第二级协程查询到的第二级协程状态:	running
第二级协程退出了
第一级协程退出了

resumeyeild传递参数的规则

 &emsp首先看一下这2个函数的介绍

---
--- Starts or continues the execution of coroutine `co`. The first time you
--- resume a coroutine, it starts running its body. The values `val1`, ...
--- are passed as the arguments to the body function. If the coroutine has
--- yielded, `resume` restarts it; the values `val1`, ... are passed as the
--- results from the yield.
---
--- If the coroutine runs without any errors, `resume` returns **true** plus any
--- values passed to `yield` (when the coroutine yields) or any values returned
--- by the body function (when the coroutine terminates). If there is any error,
--- `resume` returns **false** plus the error message.
---@overload fun(co:thread):boolean|any
---@param co thread
---@param val1 string
---@return thread|any
---
---
---启动或者继续执行协程`co`.当第一次对一个协程调用resume时,它开始运行它的函数体.除`co`
---外的参数,`val1`, `...`都会传递给协程的函数体.如果协程已经让出执行权,`resume`
---调用将重新恢复协程的执行;此时传递进去的参数`val1`, `...`将作为yield的返回值。
---
---如果协程运行正常,`resume`返回**true**外加传递给`yield`的任何值(当协程让出执行权时).
---当出错时,`resume`返回**false**外加错误消息.
function coroutine.resume(co, val1, ...) end

---
--- Suspends the execution of the calling coroutine. Any arguments to `yield`
--- are passed as extra results to `resume`.
---
---挂起调用协程的执行.传递给`yield`的所有参数都将作为`resume`的返回值
---@return any
function coroutine.yield(...) end

结合下面这个例子

#!/usr/bin/lua

local co = coroutine.create(function(p1, p2)
    print("传递给协程主函数体的参数:", p1, p2)
    while true do
        local yieldRet;
        yieldRet = coroutine.yield("c", "d")
        print("协程第一次调用yield的返回值列表:", yieldRet)
        local coRet = "f"
        return coRet
    end
end)

local resRet, value1, value2 = coroutine.resume(co, "a", "b")
print("第一次调用resume的返回值列表:", resRet, value1, value2)

resRet, value1 = coroutine.resume(co, "e")
print("第二次调用resume的返回值列表:", resRet, value1)

resRet, value1 = coroutine.resume(co, "g")
print("第三次调用resume的返回值列表:", resRet, value1)

执行结果

传递给协程主函数体的参数:	a	b
第一次调用resume的返回值列表:	true	c	d
协程第一次调用yield的返回值列表:	e
第二次调用resume的返回值列表:	true	f
第三次调用resume的返回值列表:	false	cannot resume dead coroutine

  结合例子,分析过程:

  • 入口程序第一次调用resume,激活协程,进入协程函数体时:这时进行了一次执行权的切换,resume的所有参数"a", "b"(除了第一个thread类型参数),都传递给了协程函数体的参数p1, p2
  • 协程执行序列调用yield让出执行权,执行序列回到入口函数体resume调用的返回前夕, yield的参数"c", "d"将作为resume的从第二开始的返回值列表,执行权回到入口函数体。
  • 入口函数体继续执行序列,第二次调用resume,执行权再次回协程执行序列,此时协程执行序列位于上一步yield调用的返回前夕,resume传递的参数e就作为了yield的返回值,协程执行序列继续执行。
  • 协程函数体执行到返回,让出执行权,返回值作为上一步入口函数体执行序列的第二次resume调用的返回值,执行序列再次回到入口函数体,此时协程死亡,状态变成dead
  • 入口函数体再次执行resume,由于此时协程已经死了,所以resume执行结果是失败的,返回执行结果false和错误信息
posted @ 2024-04-03 10:07  thammer  阅读(6)  评论(0编辑  收藏  举报