resume 和 yield
使用 resume/yield 在调用方和协程之间交换数据。
resume 和 yield 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。
向协程传递值
传递给第一次 coroutine.resume 调用的参数,会成为协程主体函数的参数。传递给后续恢复调用的参数,会由暂停协程的 coroutine.yield() 调用返回。
local co = coroutine.create(function(a, b)
print("received:", a, b) -- 10 20
local c = coroutine.yield() -- pauses
print("after yield:", c) -- 30
end)
coroutine.resume(co, 10, 20) -- first call: passes a,b
coroutine.resume(co, 30) -- subsequent: passed to yield来自 yield 的值
传递给 coroutine.yield(val1, val2) 的值,会成为在该 yield 处暂停的 coroutine.resume() 调用的返回值。因此,yield 会向调用方“返回”值。
local co = coroutine.create(function()
coroutine.yield(1, "hello")
coroutine.yield(2, "world")
return 3, "done"
end)
local ok, a, b = coroutine.resume(co)
print(ok, a, b) -- true 1 hello
ok, a, b = coroutine.resume(co)
print(ok, a, b) -- true 2 world
ok, a, b = coroutine.resume(co)
print(ok, a, b) -- true 3 done双向通信
协程支持真正的双向通信:调用方通过 resume 传入数据,协程处理数据后通过 yield 返回结果。每次 resume/yield 都是一次同步交换。
local co = coroutine.create(function(x)
while true do
x = coroutine.yield(x * x) -- yield x^2, get next x
end
end)
coroutine.resume(co) -- start (x=nil initially)
local ok, sq
ok, sq = coroutine.resume(co, 3); print(sq) -- 9
ok, sq = coroutine.resume(co, 5); print(sq) -- 25
ok, sq = coroutine.resume(co, 7); print(sq) -- 49生产者-消费者
这是协程的经典模式:生产者一次 yield 一个项目;消费者需要下一个项目时,就恢复生产者。双方都不会超前运行——这能实现完美同步。
local function producer(items)
return coroutine.create(function()
for _, item in ipairs(items) do
coroutine.yield(item)
end
end)
end
local pro = producer({"apple","banana","cherry"})
-- Consumer drives the loop
while true do
local ok, item = coroutine.resume(pro)
if not ok or item == nil then break end
print("consumed:", item)
end使用协程构建流水线
可以将多个协程连接成一条流水线。每个阶段从前一个阶段读取数据,并将数据 yield 给下一个阶段。最后一个阶段通过恢复自身来驱动流水线,而恢复操作会沿链条逐级传回。
local function source(items)
return coroutine.wrap(function()
for _, v in ipairs(items) do coroutine.yield(v) end
end)
end
local function filter(iter, pred)
return coroutine.wrap(function()
for v in iter do
if pred(v) then coroutine.yield(v) end
end
end)
end
local function map(iter, fn)
return coroutine.wrap(function()
for v in iter do coroutine.yield(fn(v)) end
end)
end
local pipeline = map(filter(source({1,2,3,4,5,6}),
function(n) return n%2==0 end),
function(n) return n*n end)
for v in pipeline do io.write(v.." ") end
print() -- 4 16 36使用协程模拟异步操作
无需使用 OS 线程,协程也可以模拟异步操作:在“等待”时 yield,在“结果”准备好后恢复。调度器会使用该结果恢复协程。
local scheduler = {}
local current = nil
local function await(key)
coroutine.yield(key) -- tell scheduler what we need
return coroutine.yield() -- wait for result
end
local co = coroutine.create(function()
local data = await("fetch:users")
print("Got users:", data)
end)
-- Start coroutine
local ok, want = coroutine.resume(co)
print("Coroutine wants:", want) -- fetch:users
-- Simulate delivering the result:
coroutine.resume(co, {{name="Alice"},{name="Bob"}})在函数内部使用 yield
您可以从协程调用的任何函数内部执行 yield,而不只是从顶层函数中执行。yield 会沿着所有嵌套调用向上传递,直到回到 resume。这称为“挂起的调用栈”。
local function yieldTwice(label)
coroutine.yield(label .. ":1")
coroutine.yield(label .. ":2")
end
local co = coroutine.create(function()
yieldTwice("A")
yieldTwice("B")
end)
for i = 1, 4 do
local ok, v = coroutine.resume(co)
print(v)
end
-- A:1, A:2, B:1, B:2协程不是线程
Lua 协程不是 OS 线程。任何时刻只能运行一个协程;不存在抢占或并行执行。所有协程共享一个 OS 线程。若要实现真正的并行执行,您需要在不同的 OS 线程中使用多个 Lua 状态。
-- This is NOT parallel - interleaved execution
local function task(name, n)
for i = 1, n do
print(name, i)
coroutine.yield()
end
end
local t1 = coroutine.create(function() task("A", 3) end)
local t2 = coroutine.create(function() task("B", 3) end)
-- Must explicitly schedule:
for _ = 1, 3 do
coroutine.resume(t1)
coroutine.resume(t2)
end
-- A1, B1, A2, B2, A3, B3 (interleaved, NOT parallel)恢复后的状态
请检查协程状态,以决定是否再次恢复。已经完成(从主体函数返回)的协程处于“已结束”状态——恢复它会返回 false 和错误信息。在循环中恢复协程前,请务必检查其状态。
local co = coroutine.create(function()
for i = 1, 3 do coroutine.yield(i) end
end)
while coroutine.status(co) ~= "dead" do
local ok, val = coroutine.resume(co)
if ok and val ~= nil then
print("val:", val)
end
end
-- val: 1, val: 2, val: 3一次恢复中的多次 yield
一次 coroutine.resume 调用会运行协程,直到遇到下一个 yield 或协程结束。您无法让协程在一次恢复中 yield 多次——每次 yield 都会暂停协程,并等待下一次恢复。
local co = coroutine.create(function()
local x = coroutine.yield("first")
print("got:", x)
local y = coroutine.yield("second")
print("got:", y)
end)
local _, v1 = coroutine.resume(co) -- starts, yields "first"
print("yielded:", v1) -- first
local _, v2 = coroutine.resume(co, "a") -- passes "a", yields "second"
print("yielded:", v2) -- second
coroutine.resume(co, "b") -- passes "b", coroutine ends快速检查
传递给 coroutine.yield(v1, v2) 的值会变成什么?
回顾:resume 和 yield
总结:
- 第一次恢复的参数 → 协程主体函数的参数
- 后续恢复的参数 → 由前一次 yield() 返回
- yield 的参数 → 触发该 yield 的 resume() 调用的返回值
- 双向通信:每次 resume/yield 都是一次交换
- 可以从任意嵌套函数中执行 yield
- 协程是协作式的,而不是并行的
常见问题解答
「resume 和 yield」课时是免费的吗?
是的 — 「resume 和 yield」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。
「resume 和 yield」这节课中我会学到什么?
使用 resume/yield 在调用方和协程之间交换数据。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Lua Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「resume 和 yield」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Lua Academy 课中编写并运行代码吗?
能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。