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行为序列
资源受限环境 低内存开销(对比线程)
相关推荐
笙枫3 小时前
LangGraph Agent 架构基础:从概念到第一个可运行的Agent
开发语言·架构·php
csbysj20204 小时前
DOM 验证
开发语言
superman超哥4 小时前
Rust 表达式与语句的区别:函数式思维与控制流设计
开发语言·后端·rust·rust表达式·rust语句·函数式思维·控制流设计
趁月色小酌***4 小时前
JAVA 知识点总结5
java·开发语言·python
05大叔4 小时前
SpringMVCDay01
java·开发语言
代码游侠4 小时前
复习——网络测试工具
linux·开发语言·网络·笔记·学习·测试工具
Felven4 小时前
C. Contrast Value
c语言·开发语言·算法
我的xiaodoujiao4 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 37--测试报告 Allure 前置步骤-配置安装 JDK 详细图文教程
java·开发语言·学习·测试工具
老华带你飞4 小时前
婚纱摄影网站|基于java + vue婚纱摄影网站系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot