CHUAN2 DEV ENGINE
996 正版授权研发中心 · 360 授权合作教学中心 · 抖音传奇直播合作授权 · 快手推广运营商授权
OFFICIAL LICENSED ACADEMY 查验官方授权证书 →
// 威海旷世互娱教学基地 · 技术文章
性能调优Lua进阶

【性能调优】协程 coroutine:调度原理与分帧实战

2026-09-20 07:41 作者:996 技术组 1 阅读 996引擎Lua教程传奇脚本
完整课程入口:996 全套课程体系Lua 学习路径幂尔框架 mirs.cn

协程不是多线程

Lua 的协程(coroutine)是协作式"用户态线程":同一时刻只有一个协程在跑,切换点完全由你自己用 coroutine.yield 声明。它没有锁、没有竞态,是游戏脚本里做"分帧执行、异步流程"最安全的工具——这也是它和真线程的本质区别。

四个核心 API 构成全部生命周期:create 创建后返回一个 thread 对象但不执行;resume 启动或恢复它,一路跑到 yield 或结束;yield 挂起并把值带回给 resume;status 查看当前是 suspended、running 还是 dead。

lua
local co = coroutine.create(function(total)
    for i = 1, total do
        coroutine.yield(i)
    end
    return "done"
end)
release_print(coroutine.resume(co))  -- true  1
release_print(coroutine.resume(co))  -- true  2
release_print(coroutine.resume(co))  -- true  done 字符串

用它把重活切成小块

批量刷 5000 个怪物的数据初始化如果一口气做完,主循环会卡死一帧。用协程把工作切片,每帧只处理一批:

lua
local worker = coroutine.create(function(list)
    for i, cfg in ipairs(list) do
        initMonster(cfg)
        if i % 200 == 0 then coroutine.yield() end  -- 每 200 个让出一次
    end
end)

-- 挂进引擎的循环触发器里,每帧 resume 一次
local ok = coroutine.resume(worker, monsterList)
if coroutine.status(worker) == "dead" then
    release_print("初始化完成")
end

这就是最朴素的"分帧器"骨架:进度天然保存在协程内部,不需要额外的游标变量,中断与恢复都由调度器一句话完成。

异步流程与错误处理

协程的另一个高价值场景是把"回调地狱"拉直。把"等待玩家确认""等待服务器返回"封装成 yield 点,整段业务就能写成顺序代码,可读性远高于层层嵌套的回调。

错误处理要记住两条:协程内的 Lua 错误不会向外抛,而是通过 resume 的第一个返回值(false)和错误消息带回,所以每次 resume 都要检查 ok;需要完整栈信息时,在协程体内用 xpcall(主逻辑, debug.traceback) 包一层,resume 拿到的错误串就带着出错位置,排查联动功能时能省一半时间。

最后一个实用细节:协程对象本身有开销,但很小,按任务粒度创建即可;真正要避免的是在 yield 之后把宿主数据悄悄改掉——恢复点之间数据结构发生了变化,是协程逻辑出 bug 的最大来源,写代码时保持"yield 前后假设世界会变"的警惕即可。

← 返回文章地图返回研学路径