0Pricing
Lua Academy · 课时

创建协程

使用 coroutine.create 和 coroutine.wrap 创建协程。

创建协程 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。

什么是协程?

协程是一种可以暂停执行(yield),并在之后从暂停位置继续执行的函数。与线程不同,协程是协作式的——同一时间只有一个协程运行,并且必须显式让出控制权。Lua 协程拥有自己的栈和局部变量。

local co = coroutine.create(function()
  print("start")
  coroutine.yield()
  print("resumed")
  coroutine.yield()
  print("done")
end)

print(coroutine.status(co))   -- suspended
coroutine.resume(co)          -- start
print(coroutine.status(co))   -- suspended
coroutine.resume(co)          -- resumed

coroutine.create 与 coroutine.wrap

coroutine.create(fn) 会创建一个协程,并返回协程对象(类型为 thread)。coroutine.wrap(fn) 会创建一个协程,并返回一个函数;调用该函数时,协程会恢复执行。wrap 更简单,但会隐藏协程对象。

-- create: returns coroutine object
local co = coroutine.create(function(x)
  return x * 2
end)
local ok, val = coroutine.resume(co, 5)
print(ok, val)   -- true  10

-- wrap: returns a resumable function
local gen = coroutine.wrap(function(x)
  return x * 3
end)
print(gen(5))    -- 15

协程生命周期

协程有四种状态:挂起(未运行)、运行中(当前正在执行)、已结束(已返回或发生错误),以及普通(由于恢复了另一个协程而暂停)。使用 coroutine.status(co) 检查状态。

local co = coroutine.create(function()
  coroutine.yield()
end)

print(coroutine.status(co))  -- suspended
coroutine.resume(co)          -- runs until yield
print(coroutine.status(co))  -- suspended again
coroutine.resume(co)          -- runs until end
print(coroutine.status(co))  -- dead

运行多个协程

创建多个协程,依次恢复每个协程,以交错方式运行它们。这样可以模拟并发执行(协作式多任务处理)。每个协程都会在让出控制权期间保留自己的状态。

local function worker(name, steps)
  for i = 1, steps do
    print(name .. ": step " .. i)
    coroutine.yield()
  end
end

local co1 = coroutine.create(function() worker("A", 3) end)
local co2 = coroutine.create(function() worker("B", 3) end)

for _ = 1, 3 do
  coroutine.resume(co1)
  coroutine.resume(co2)
end
-- A:1, B:1, A:2, B:2, A:3, B:3

coroutine.wrap 迭代

coroutine.wrap 常用于创建迭代器。迭代器每前进一次,就调用一次返回的函数。当协程主体结束时,该函数会引发错误——如有需要,请使用 pcall 或检查状态。

local function range(n)
  return coroutine.wrap(function()
    for i = 1, n do
      coroutine.yield(i)
    end
  end)
end

for v in range(5) do
  io.write(v .. " ")
end
print()  -- 1 2 3 4 5

协程中的错误

如果协程引发错误,coroutine.resume 会返回 false,后面跟着错误消息。该协程会转换为 dead 状态。已结束的协程无法再次恢复。

local co = coroutine.create(function(x)
  if x < 0 then error("negative input: " .. x) end
  return math.sqrt(x)
end)

local ok, val = coroutine.resume(co, -1)
print(ok, val)    -- false  input:2: negative input: -1
print(coroutine.status(co))  -- dead

-- Can't resume a dead coroutine:
ok, val = coroutine.resume(co)
print(ok, val)    -- false  cannot resume dead coroutine

协程身份

coroutine.running() 会返回当前正在运行的协程,以及一个表示它是否为主线程的布尔值。在协程内部使用它可以获取对自身的引用。

local co = coroutine.create(function()
  local self, isMain = coroutine.running()
  print("type:", type(self))      -- thread
  print("isMain:", isMain)        -- false
  print("status:", coroutine.status(self)) -- running
end)

coroutine.resume(co)

-- In main thread:
local main, isMain = coroutine.running()
print("main isMain:", isMain)    -- true

无限生成器

协程主体可以包含一个内部带有 yield 的无限循环——生成器按需生成值,而不会终止。这是协程的一个重要用途:生成延迟计算的无限序列。

local function naturals(start)
  return coroutine.wrap(function()
    local n = start or 1
    while true do
      coroutine.yield(n)
      n = n + 1
    end
  end)
end

local gen = naturals(1)
for i = 1, 8 do
  io.write(gen() .. " ")
end
print()  -- 1 2 3 4 5 6 7 8

将协程作为任务

一种基本的任务调度器是:将协程存储在队列中,然后依次恢复每个协程。当协程让出控制权时,将其放回队列;当协程结束时,将其移除。这是一个最小化的协作式调度器。

local tasks = {}

local function spawn(fn)
  tasks[#tasks+1] = coroutine.create(fn)
end

local function run()
  while #tasks > 0 do
    local alive = {}
    for _, co in ipairs(tasks) do
      coroutine.resume(co)
      if coroutine.status(co) ~= "dead" then
        alive[#alive+1] = co
      end
    end
    tasks = alive
  end
end

spawn(function() for i=1,3 do print("T1:"..i); coroutine.yield() end end)
spawn(function() for i=1,3 do print("T2:"..i); coroutine.yield() end end)
run()

协程内存

每个协程都有自己的栈。对于很深的调用链,栈可能会增长得很大。在高性能场景中,应避免创建成千上万个同时存活的协程。请尽可能重用协程,或使用协程池。

-- Coroutine pool concept
local pool = {}

local function getCoroutine(fn)
  -- Reuse dead coroutine slots
  for i, co in ipairs(pool) do
    if coroutine.status(co) == "dead" then
      pool[i] = coroutine.create(fn)
      return pool[i]
    end
  end
  local co = coroutine.create(fn)
  pool[#pool+1] = co
  return co
end

print("Pool pattern for coroutine reuse")

快速检查

在执行 coroutine.create(fn) 后,协程立即处于什么状态?

回顾:创建协程

总结:

  • coroutine.create(fn) → 协程对象;wrap(fn) → 可恢复的函数
  • 状态:挂起 → 运行 → 挂起/已结束
  • coroutine.resume(co, ...) 启动或恢复协程
  • 恢复期间发生错误:返回 false 和错误消息;协程变为已结束
  • 通过 while true do yield() end 创建无限生成器
  • 用于协作式多任务处理和惰性序列

常见问题解答

「创建协程」课时是免费的吗?

是的 — 「创建协程」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。

「创建协程」这节课中我会学到什么?

使用 coroutine.create 和 coroutine.wrap 创建协程。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Lua Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「创建协程」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Lua Academy 课中编写并运行代码吗?

能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

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