协程是什么
可以主动暂停、之后从断点恢复的函数。不是线程——它在单线程内手动切换。
local co = coroutine.create(function(a, b)
print("start", a, b)
local c = coroutine.yield(a + b) -- 暂停,返回 a+b 给外面
print("resumed with", c)
return "done"
end)
print(coroutine.resume(co, 1, 2)) -- start 1 2 → true 3
print(coroutine.resume(co, 99)) -- resumed with 99 → true done
print(coroutine.resume(co)) -- 已死,返回 false
三个核心函数
| 作用 | |
|---|---|
coroutine.create(fn) |
创建协程对象(不立即跑) |
coroutine.resume(co, args) |
启动 / 恢复运行 |
coroutine.yield(values) |
协程内部:暂停 + 返回 |
resume 返回 true, yielded_values...;如果协程出错 false, errmsg。
4 个状态
local co = coroutine.create(function() coroutine.yield() end)
print(coroutine.status(co)) -- "suspended" 刚创建 / yield 后
coroutine.resume(co)
print(coroutine.status(co)) -- "suspended" yield 了
coroutine.resume(co)
print(coroutine.status(co)) -- "dead" 跑完了
"running" 状态只能从协程内部看到(调 coroutine.status(coroutine.running()))。
用途 1:生成器
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 print(v) end -- 1 2 3 4 5
coroutine.wrap(fn) 返回一个每次调用就 resume 一次的函数——配合 for 写迭代器超清爽。
用途 2:异步 I/O 风格化
local function fake_async()
local ok, data = coroutine.yield("read", "file.txt")
return data
end
-- 调度器
local co = coroutine.create(fake_async)
local ok, op, arg = coroutine.resume(co)
-- 假设这里去做真异步读,完成后...
coroutine.resume(co, true, "file contents")
OpenResty / cqueues / luv 等异步框架背后就是这种调度。
用途 3:状态机
local function door()
while true do
coroutine.yield("closed")
coroutine.yield("opening")
coroutine.yield("open")
coroutine.yield("closing")
end
end
local co = coroutine.create(door)
for i = 1, 6 do
local _, state = coroutine.resume(co)
print(state)
end
-- closed / opening / open / closing / closed / opening
yield 不能跨 C 边界
在 C 函数(如 pcall、xpcall、回调进 C 库再调 Lua)里 yield 会失败:
attempt to yield across C-call boundary
5.2+ 有 coroutine.isyieldable() 判断能不能 yield,以及 pcall 在 5.2 起改成 yieldable。Lua 5.1 / LuaJIT 仍受此限。
协程不并行
-- 错觉:以为这是并发
local co1 = coroutine.create(work1)
local co2 = coroutine.create(work2)
coroutine.resume(co1)
coroutine.resume(co2)
一次只有一个在跑——协程是协作式调度,不是抢占式线程。要并行需要 lua-lanes 或 effil 多 Lua 状态机方案。
心智模型
把协程当成可以暂停的函数。它的核心价值是把"看起来同步、其实在等待"的代码写得像普通顺序代码——而不是把一堆回调嵌起来。
-- 没有协程:
fetch("/a", function(a)
fetch("/b", function(b)
fetch("/c", function(c)
print(a, b, c)
end)
end)
end)
-- 有协程:
local a = await(fetch("/a"))
local b = await(fetch("/b"))
local c = await(fetch("/c"))
print(a, b, c)
其中 await 通常实现为 coroutine.yield,调度器负责异步完成时 resume。
→ 下一篇 string 库