0Pricing
Lua Academy · 课时

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 创建协程
  2. resume 和 yield
  3. 协程状态
  4. 生产者-消费者模式
← 返回 Lua Academy