Lua: 协程编程详解之从基础到多任务处理与应用实战

协程基础与核心概念

1 ) 定义与特点

轻量级线程:在单线程内实现多任务协作,由程序显式控制切换(非抢占式)

资源开销低:每个协程独立堆栈(通常KB级),远低于操作系统线程(MB级)

适用场景:I/O密集型任务、状态机、游戏AI、异步逻辑等

2 ) 协程 vs 多线程

特性 协程 多线程
调度权 程序显式控制(yield) 操作系统抢占式调度
并发性 协作式单核并发 并行/多核并发
数据同步 无需锁(单线程内) 需互斥锁/信号量

2.1 示例:创建与启动协程

lua 复制代码
local co = coroutine.create(function(a, b)
    print("协程执行:", a, b)
    local result = coroutine.yield(a + b)  -- 挂起并返回a+b
    print("恢复执行,接收参数:", result)
    return "结束"
end)

local ok, sum = coroutine.resume(co, 10, 20)  -- 启动协程,传入参数
print("首次resume结果:", ok, sum)             -- 输出: true, 30

ok, msg = coroutine.resume(co, "新数据")      -- 恢复协程,传入"新数据"
print("二次resume结果:", ok, msg)             -- 输出: true, "结束"

2.2 示例:多个协程协作处理任务

lua 复制代码
local co1 = coroutine.create(function()
    for i = 1, 3 do
        print("协程1执行步骤", i)
        coroutine.yield() -- 暂停,让出控制权
    end
    print("协程1完成")
end)

local co2 = coroutine.create(function()
    for i = 1, 3 do
        print("协程2执行步骤", i)
        coroutine.yield() -- 暂停,让出控制权
    end
    print("协程2完成")
end)

--- 主线程调度两个协程交替执行
print("开始执行协程1")
coroutine.resume(co1)
print("开始执行协程2")
coroutine.resume(co2)
print("再次执行协程1")
coroutine.resume(co1)
print("再次执行协程2")
coroutine.resume(co2)
print("第三次执行协程1")
coroutine.resume(co1)
print("第三次执行协程2")
coroutine.resume(co2)

3 ) 协程生命周期与关键函数

3.1 状态流转
coroutine.create
coroutine.resume
yield
执行完毕
创建
Suspended
Running
Dead

3.2 核心函数解析

  • coroutine.create(f)

    • 创建协程,返回thread对象(状态为suspended)
  • coroutine.resume(co, ...)

    • 启动或恢复一个协程的执行
    • 首次调用:参数传递给协程函数
    • 后续调用:参数作为yield的返回值
    • 如果协程处于挂起或正常(normal)状态,resume会使其运行
    • 当协程执行到yield或结束时,控制权返回给resume的调用者
  • coroutine.yield(...):

    • 在协程内部调用,使协程暂停执行并返回到resume调用处
    • 协程的状态变为"挂起"
    • 挂起协程,参数作为resume的返回值
    • 恢复时接收resume传入的新参数
  • 状态检查示例

    lua 复制代码
    print(coroutine.status(co))  -- 输出: suspended(创建后) → running(执行中) → dead(结束)

示例代码

lua 复制代码
--- 示例:演示协程生命周期
local co = coroutine.create(function(a, b)
    print("协程开始执行,参数:", a, b)
    local result = coroutine.yield("中间结果") -- 暂停并返回值
    print("协程恢复执行,从yield接收到:", result)
    return "最终结果"
end)

print("协程状态:", coroutine.status(co)) -- suspended

print("启动协程:")
local success, value = coroutine.resume(co, 10, 20) -- 传递参数给协程主函数
print("resume返回:", success, value) -- true, "中间结果"
print("协程状态:", coroutine.status(co)) -- suspended

print("再次启动协程:")
success, value = coroutine.resume(co, "恢复数据") -- 传递数据给yield
print("resume返回:", success, value) -- true, "最终结果"
print("协程状态:", coroutine.status(co)) -- dead

4 ) 协程与多任务处理

4.1 协作式多任务原理

事件循环调度:主线程轮询唤醒多个协程,避免阻塞5。

适用场景:网络请求批处理、游戏NPC行为序列26。

lua 复制代码
--- 模拟多任务调度器
local tasks = {}
local function add_task(f) table.insert(tasks, coroutine.create(f)) end
-- 任务示例:模拟耗时操作
add_task(function()
    for i = 1, 3 do
        print("Task1: step", i)
        coroutine.yield()
    end
end)

add_task(function()
    for i = 1, 2 do
        print("Task2: step", i)
        coroutine.yield()
    end
end)
-- 调度执行
while #tasks > 0 do
    for i = #tasks, 1, -1 do
        local ok = coroutine.resume(tasks[i])
        if coroutine.status(tasks[i]) == "dead" then
            table.remove(tasks, i)
        end
    end
end
-- 输出交替执行步骤:Task1:step1 → Task2:step1 → Task1:step2 → ...

5 ) 协程在并发模型中的应用

5.1 生产者-消费者模式

lua 复制代码
local function producer()
    local i = 0
    return function()  -- 迭代器工厂
        i = i + 1
        if i <= 5 then
            coroutine.yield("数据-" .. i)  -- 生产数据并挂起
        end
    end
end

local co_producer = coroutine.wrap(producer())  -- 简化resume调用

local function consumer()
    while true do
        local data = co_producer()
        if not data then break end
        print("消费:", data)  -- 输出: 数据-1, 数据-2, ...
    end
end

consumer()

5.2 非阻塞I/O处理(模拟)

lua 复制代码
--- 异步HTTP请求模拟
local function async_http(url, callback)
    print("发起请求:", url)
    coroutine.yield()  -- 挂起协程,等待响应
    callback("响应数据:" .. url)
end

--- 协程管理多个请求
coroutine.wrap(function()
    async_http("api/user", function(data) print(data) end)
    async_http("api/orders", function(data) print(data) end)
end)()

--- 事件循环模拟响应
for _ = 1, 2 do
    coroutine.resume(main_co)  -- 恢复协程,触发回调
end

6 ) 高级技巧与陷阱

6.1 协程复用

lua 复制代码
local worker = coroutine.create(function(f, args)
    while f do
        f, args = coroutine.yield(f(table.unpack(args)))
    end
end)
--- 复用协程执行不同函数
coroutine.resume(worker, math.abs, {-10})  -- 输出: 10
coroutine.resume(worker, string.upper, {"hello"})  -- 输出: HELLO

6.2 错误处理

pcall包裹resume捕获异常:

lua 复制代码
local ok, err = pcall(coroutine.resume, co, bad_arg)

总结

场景 协程优势
I/O密集型任务 避免回调地狱,代码线性化
游戏逻辑 简化状态机,NPC行为序列
资源受限环境 低内存开销(对比线程)
相关推荐
垆边人似月.7 分钟前
华为题:字符串中最长连续数字串
开发语言·c++·算法
星夜离尘5613 分钟前
MATLAB 清洗 CSV 采样日志:重复时间戳、无效行、分组统计与自动出图
开发语言·matlab·数据分析·数据清洗·csv
程序猿追18 分钟前
立方体转到九十度:用 kotlin-math 核对鸿蒙上的两种旋转
android·开发语言·kotlin
nagualky12321 分钟前
GPT-4 系列退役在即:一套可回滚的模型迁移验收法
java·开发语言·ai·架构
Ai-_Man24 分钟前
DeepSeek生成的内容能否下载吗?
开发语言·javascript·人工智能·小程序·ecmascript
Evelvnk25 分钟前
计算机学习资料
开发语言·人工智能·计算机·php
geovindu36 分钟前
rust: Chain of Responsibility Pattern
开发语言·后端·设计模式·rust·责任链模式·行为模式
阿里嘎多学长39 分钟前
2026-10-14 GitHub 热点项目精选
开发语言·程序员·github·代码托管
迪康coolmu39 分钟前
迪康Defender网络准入控制系统落地实战:部署、配置与问题排查记录
运维·开发语言·网络·经验分享·php
兰亭妙微UI设计公司40 分钟前
兰亭妙微分享:高手设计登录页的12种切入点
开发语言·前端·javascript