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和错误信息