协程 #

一、协程概述 #

1.1 什么是协程 #

协程是一种轻量级的并发机制,也称为"协作式多任务"。与线程不同,协程由程序自己控制切换,而不是由操作系统调度。

1.2 协程的特点 #

  • 轻量:创建成本低,内存占用小
  • 协作式:由程序控制切换时机
  • 非抢占:不会被强制中断
  • 单线程:同一时刻只有一个协程执行

1.3 协程状态 #

lua
-- 协程有四种状态
-- suspended:挂起(初始状态)
-- running:运行中
-- normal:正常(唤醒其他协程时)
-- dead:结束

二、协程基础 #

2.1 创建协程 #

lua
-- 方式1:coroutine.create
local co = coroutine.create(function()
    print("协程执行")
end)

print(coroutine.status(co))  -- suspended

-- 方式2:coroutine.wrap(返回函数)
local cofunc = coroutine.wrap(function()
    print("协程执行")
end)

cofunc()  -- 直接调用

2.2 启动和恢复协程 #

lua
local co = coroutine.create(function()
    print("协程开始")
    coroutine.yield()
    print("协程继续")
end)

print(coroutine.status(co))  -- suspended

coroutine.resume(co)  -- 协程开始
print(coroutine.status(co))  -- suspended

coroutine.resume(co)  -- 协程继续
print(coroutine.status(co))  -- dead

2.3 yield 和 resume #

lua
local co = coroutine.create(function()
    for i = 1, 5 do
        print("协程:" .. i)
        coroutine.yield(i)
    end
    return "完成"
end)

for i = 1, 6 do
    local ok, value = coroutine.resume(co)
    print("主线程收到:" .. tostring(value))
end
-- 协程:1
-- 主线程收到:1
-- 协程:2
-- 主线程收到:2
-- ...

2.4 传递参数 #

lua
-- resume 传递参数给协程
local co = coroutine.create(function(x, y)
    print("收到:" .. x .. ", " .. y)
    local a, b = coroutine.yield(x + y)
    print("继续收到:" .. a .. ", " .. b)
    return a * b
end)

local ok, result = coroutine.resume(co, 10, 20)
print("结果:" .. result)  -- 30

ok, result = coroutine.resume(co, 3, 4)
print("结果:" .. result)  -- 12

三、协程函数 #

3.1 coroutine.create #

lua
-- 创建协程,返回协程对象
local co = coroutine.create(function()
    print("Hello")
end)

3.2 coroutine.resume #

lua
-- 恢复协程执行
local co = coroutine.create(function()
    return "result"
end)

local ok, result = coroutine.resume(co)
print(ok, result)  -- true    result

3.3 coroutine.yield #

lua
-- 挂起协程
local co = coroutine.create(function()
    local x = coroutine.yield("first")
    print("收到:" .. x)
    return "done"
end)

local ok, value = coroutine.resume(co)
print(value)  -- first

ok, value = coroutine.resume(co, "hello")
-- 收到:hello
print(value)  -- done

3.4 coroutine.status #

lua
-- 获取协程状态
local co = coroutine.create(function()
    coroutine.yield()
end)

print(coroutine.status(co))  -- suspended
coroutine.resume(co)
print(coroutine.status(co))  -- suspended
coroutine.resume(co)
print(coroutine.status(co))  -- dead

3.5 coroutine.running #

lua
-- 获取当前运行的协程
local current_co
local co = coroutine.create(function()
    current_co = coroutine.running()
    print("当前协程:" .. tostring(current_co))
end)

coroutine.resume(co)
print("是否是主线程:" .. tostring(current_co == nil))

3.6 coroutine.wrap #

lua
-- 创建协程并返回函数
local func = coroutine.wrap(function()
    for i = 1, 3 do
        coroutine.yield(i)
    end
end)

print(func())  -- 1
print(func())  -- 2
print(func())  -- 3
print(func())  -- nil(协程结束)

四、协程应用 #

4.1 迭代器 #

lua
-- 使用协程创建迭代器
local function range_iter(max)
    return coroutine.wrap(function()
        for i = 1, max do
            coroutine.yield(i)
        end
    end)
end

for i in range_iter(5) do
    print(i)
end
-- 1 2 3 4 5

-- 遍历二叉树
local function traverse(tree)
    return coroutine.wrap(function()
        if tree then
            for v in traverse(tree.left) do
                coroutine.yield(v)
            end
            coroutine.yield(tree.value)
            for v in traverse(tree.right) do
                coroutine.yield(v)
            end
        end
    end)
end

4.2 生产者-消费者 #

lua
-- 生产者
local function producer()
    return coroutine.create(function()
        for i = 1, 10 do
            print("生产:" .. i)
            coroutine.yield(i)
        end
    end)
end

-- 消费者
local function consumer(prod)
    while true do
        local ok, value = coroutine.resume(prod)
        if not ok or not value then
            break
        end
        print("消费:" .. value)
    end
end

local prod = producer()
consumer(prod)

4.3 任务调度器 #

lua
local Scheduler = {}
Scheduler.__index = Scheduler

function Scheduler:new()
    return setmetatable({tasks = {}}, self)
end

function Scheduler:add(func)
    local co = coroutine.create(func)
    table.insert(self.tasks, co)
    return co
end

function Scheduler:run()
    while #self.tasks > 0 do
        local co = table.remove(self.tasks, 1)
        local ok, delay = coroutine.resume(co)
        
        if ok and coroutine.status(co) ~= "dead" then
            if delay then
                -- 延迟任务
                table.insert(self.tasks, co)
            else
                -- 立即重新调度
                table.insert(self.tasks, 1, co)
            end
        end
    end
end

-- 使用
local scheduler = Scheduler:new()

scheduler:add(function()
    for i = 1, 3 do
        print("任务A:" .. i)
        coroutine.yield(true)  -- 让出控制权
    end
end)

scheduler:add(function()
    for i = 1, 3 do
        print("任务B:" .. i)
        coroutine.yield(true)
    end
end)

scheduler:run()
-- 任务A:1
-- 任务B:1
-- 任务A:2
-- 任务B:2
-- 任务A:3
-- 任务B:3

4.4 异步操作模拟 #

lua
local Async = {}

function Async.sleep(ms)
    coroutine.yield({type = "sleep", duration = ms})
end

function Async.fetch(url)
    coroutine.yield({type = "fetch", url = url})
end

local function run_async(func)
    local co = coroutine.create(func)
    local ok, op
    
    local function resume(result)
        ok, op = coroutine.resume(co, result)
        if ok and coroutine.status(co) ~= "dead" then
            if op.type == "sleep" then
                -- 模拟异步等待
                local timer = os.time() + op.duration / 1000
                while os.time() < timer do end
                resume()
            elseif op.type == "fetch" then
                -- 模拟网络请求
                resume({data = "Response from " .. op.url})
            end
        end
    end
    
    resume()
end

-- 使用
run_async(function()
    print("开始")
    Async.sleep(1000)
    print("等待后")
    local response = Async.fetch("https://api.example.com")
    print("响应:" .. response.data)
end)

4.5 状态机 #

lua
local function state_machine()
    return coroutine.wrap(function()
        while true do
            -- 状态 A
            print("状态 A")
            local input = coroutine.yield("A")
            if input == "next" then
                -- 状态 B
                print("状态 B")
                input = coroutine.yield("B")
                if input == "next" then
                    -- 状态 C
                    print("状态 C")
                    input = coroutine.yield("C")
                end
            end
        end
    end)
end

local sm = state_machine()
print("当前状态:" .. sm())       -- 状态 A
print("当前状态:" .. sm("next")) -- 状态 B
print("当前状态:" .. sm("next")) -- 状态 C

五、协程与错误处理 #

5.1 错误传播 #

lua
local co = coroutine.create(function()
    error("协程错误")
end)

local ok, err = coroutine.resume(co)
if not ok then
    print("协程错误:" .. err)
end

5.2 安全执行 #

lua
local function safe_resume(co, ...)
    local ok, result = coroutine.resume(co, ...)
    if not ok then
        print("错误:" .. result)
        return nil
    end
    return result
end

六、协程与管道 #

6.1 管道模式 #

lua
local function pipeline(...)
    local stages = {...}
    
    return function(input)
        local current = input
        for _, stage in ipairs(stages) do
            current = stage(current)
        end
        return current
    end
end

-- 使用协程实现流式处理
local function stream_processor(processor)
    return coroutine.wrap(function()
        while true do
            local input = coroutine.yield()
            if input == nil then break end
            processor(input)
        end
    end)
end

local filter = stream_processor(function(x)
    if x % 2 == 0 then
        print("偶数:" .. x)
    end
end)

for i = 1, 10 do
    filter(i)
end

七、总结 #

本章介绍了 Lua 协程:

  1. 协程概念:轻量级并发机制
  2. 协程状态:suspended、running、normal、dead
  3. 基本操作:create、resume、yield、wrap
  4. 应用场景:迭代器、生产者消费者、任务调度
  5. 异步模拟:sleep、fetch 等异步操作
  6. 状态机:使用协程实现状态转换

下一章,我们将学习 Lua 框架与应用。

最后更新:2026-03-27