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()
在理解上面例子前,需要知道yield
和resume
的作用,类比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 第二级协程退出了 第一级协程退出了
resume
和yeild
传递参数的规则
&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和错误信息
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
2019-04-03 C关键字volatile总结