协程基础与核心概念


1 ) 定义与特点

轻量级线程:在单线程内实现多任务协作,由程序显式控制切换(非抢占式)
资源开销低:每个协程独立堆栈(通常KB级),远低于操作系统线程(MB级)
适用场景:I/O密集型任务、状态机、游戏AI、异步逻辑等

2 ) 协程 vs 多线程

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

2.1 示例:创建与启动协程

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 示例:多个协程协作处理任务

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传入的新参数
  • 状态检查示例
    print(coroutine.status(co))  -- 输出: suspended(创建后) → running(执行中) → dead(结束)
    

示例代码

--- 示例:演示协程生命周期
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]。

--- 模拟多任务调度器
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 生产者-消费者模式

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处理(模拟)

--- 异步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 协程复用

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捕获异常:

local ok, err = pcall(coroutine.resume, co, bad_arg)

总结

场景 协程优势
I/O密集型任务 避免回调地狱,代码线性化
游戏逻辑 简化状态机,NPC行为序列
资源受限环境 低内存开销(对比线程)
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐