一、协程的创建和状态
1.1创建协程:coroutine.create
Lua中所有协程相关的函数都封装在coroutine表中,coroutine.create是创建协程的入口,它接收一个函数作为参数,返回一个thread类型的值,代表创建的协程。
            
            
              Lua
              
              
            
          
          --创建一个简单的协程
co = coroutine.create(function () print("hello world") end)
print(co)  ->打印thread的地址值得注意的是,创建协程不会处于运行状态,而是先到挂起状态------这是协程的初始状态,也是后续可恢复执行的前提。
1.2查看协程状态:coroutine.status
协程有四种状态:
挂起(suspended):初始状态,或者由yield挂起后的状态,可通过resmue恢复;
运行(running):协程正在执行中;
死亡(dead):协程主体函数执行完毕,或者因为错误终止,无法再次恢复;
正常(noramal):协程A唤醒协程B后,A所处的状态(非挂起也非运行)。
通过coroutine.status(协程对象)可以查看当前状态
            
            
              Lua
              
              
            
          
          co = coroutine.create(...)
print(coroutine.status(co)) ->suspend(初始状态)
coroutine.resume(co)
print(coroutine.status(co)) ->dead(运行完成)1.3挂起协程:coroutine.yield
            
            
              Lua
              
              
            
          
          co = coroutine.create(function ()
    for i=1,3 do
        print("co",i)
        coroutine.yield() --关键:到此处挂起协程
    end
end)
print(coroutine.status(co))由上述实例,第一次调用resume时的执行流程
(1)coroutine.resume(co)启动协程,执行循环的第一迭代,打印
(2)coroutine.yield() 协程挂起,执行权返回给resume
(3)此处协程仍属于挂起状态,可以通过coroutine.stutas()查看。
yeild的作用不仅仅是"暂停",更是"状态保存"------协程挂起时,局部变量的值会被保留,下次resum时从yield后面直接执行。
1.4协程状态流转

二、resume与yeild的协同,数据交换
2.1yeild的参数:成为resume的返回值
当协程通过yield(val1,val2,.....)挂起时,val1,val2,....或作为coroutine.resume的后续返回值(在true之后)
            
            
              Lua
              
              
            
          
          co = coroitine.create(function (a,b)
    coroutine.yield(a + b,a - b)
end)
--resume返回true,以及yield的参数30、10
print(coroutine.resume(co,30,10)) ->true   30   102.2resume的后续参数:成为yield的返回值
当再次调用resume恢复挂起状态的协程时,传递给resum的额外额外参数,会成为yield调用的返回值。
            
            
              Lua
              
              
            
          
          co = coroutine.create(function ()
  -- yield的返回值来自下一次resume的参数
  local x, y = coroutine.yield()
  print("co", x, y)
end)
coroutine.resume(co)  -- 第一次resume:启动协程,执行到yield挂起
coroutine.resume(co, 4, 5)  --> co 4 5 (4、5成为yield的返回值)2.3协程正常和错误的场景
resume在保护模式下运行,这意味着,即使协程执行中发生了错误,Lua也不会直接崩溃,而是将错误信息通过resume的返回值传递出来。这一特征让我们能够优雅地处理协程错误,也是resume返回值设计的核心逻辑。
场景1:协程正常执行
当协程未发生错误时。resume的返回值格式为:
true , val1 , val2 ,....
- 第一个返回值固定为
true,表示执行正常;- 后续的
val1, val2, ...分两种情况:
- 若协程因
yield挂起:返回coroutine.yield的参数(即协程希望传递给外部的值);- 若协程执行完毕:返回协程主体函数的返回值。
示例 1:协程因 yield 挂起(返回 yield 的参数)
            
            
              Lua
              
              
            
          
          co = coroutine.create(function ()
  -- yield传递两个值:10、20
  coroutine.yield(10, 20)
end)
-- resume返回true,以及yield的参数10、20
print(coroutine.resume(co))  --> true  10  20示例 2:协程执行完毕(返回主体函数的返回值)
            
            
              Lua
              
              
            
          
          co = coroutine.create(function ()
  -- 主体函数返回30、40
  return 30, 40
end)
-- resume返回true,以及主体函数的返回值30、40
print(coroutine.resume(co))  --> true  30  40场景2:协程执行出错
当协程执行发生错误,resume的返回值格式为:
false ,错误信息
- 第一个返回值固定为
false,表示执行出错;- 第二个返回值是字符串类型的错误信息,描述错误原因。
            
            
              Lua
              
              
            
          
          co = coroutine.create(function ()
  -- 故意调用未定义的函数,触发错误
  undefinedFunc()
end)
-- resume返回false,以及错误信息
print(coroutine.resume(co))  
--> false  [string "function () undefinedFunc() end"]:2: attempt to call global 'undefinedFunc' (a nil value)三、管道与过滤器
核心是利用协程的resume-yeild机制解决传统生产者消费者"谁是主导主循环"问题,并进一步扩展出"过滤器"组件,实现数据生成,处理,消费全流程协同,同是对比了与UNIX管道的异同。
3.1核心问题:传统生产者-消费者"主循环冲突"
传统的生产者消费者模型中,生产者和消费者各自拥有独立主循环,均视为对方为"可调用服务",存在"谁主导执行流程"的矛盾。
            
            
              Lua
              
              
            
          
          --生产者:不断产生值(读文件),通过send发送给消费者
function producer()
    while true do 
        local x = io.read()   --生产值
        send(x)               --发送给消费者(核心矛盾:send如何匹配receive)
    end
end
--消费者:不断通过receive接收值,然后消费
function consumer()
    while true do
        local x = receive()  --从消费者接收值(核心矛盾:receive如何触发生产者?)
        io.write(x,"\n")     --消费
    end
end核心问题:生产者和消费者均为"主动执行",无法直接匹配send与receive的调用关系------若让生产者主导,消费者需被动等待;若让消费者主导,生产者需被动响应,传统函数调用难以协调。
3.2解决方案用resmue匹配send-receive
协程的resume-yeild机制可反转"调用-被调用"关系,让send和receive都认为自己是"主动方",从而解决主循环冲突。具体通过receive和send函数,将生产者封装为协程,由消费者主导执行流程。
(1)receive和send
receive(prod) :消费者通过此函数 "唤醒生产者" 并获取值。内部调用coroutine.resume(prod)启动 / 恢复生产者协程,接收生产者通过yield传递的值。
            
            
              Lua
              
              
            
          
          function receive(prod)
    local status, value = coroutine.resume(prod)  --唤醒生产者协程
    return value  --返回生产者产生的新值
endsend(x) :生产者通过此函数 "传递值并挂起"。内部调用coroutine.yield(x),将值x传递给receive,同时挂起自身,等待下一次被唤醒。
            
            
              Lua
              
              
            
          
          function send(x)
    coroutine.yield(x) --传递值给receive,挂起生产者
end(2)生产者封装成协程
生产者不再是独立主循环,而是被封装为协程(通过coroutine.create创建),需通过receive唤醒才会执行并产生值。书中代码示例:
            
            
              Lua
              
              
            
          
          function producer()
    return coroutine.create(function()
        while true do
            local x=io.read()
            send(x)
        end
    end)
end(3)消费者主导执行
消费者通过receive主动唤醒生产者获取值,主导整个流程:当消费者需要新值时,调用receive唤醒生产者,生产者产生值后挂起,消费者继续消费。完整调用逻辑:
            
            
              Lua
              
              
            
          
          function coneumer(prod)
    while true do
        local x = receive(prod)
        io.write(x, "\n")
    end
end
--启动流程
local p = producer()
consumer(p)四、过滤器
过滤器组件:介于生产者和消费者之间,即作为"消费者"从生产者获取值,又作为"生产者"处理数据后传递给消费者,实现数据的中间转换。
过滤器的逻辑:以 "给每行数据加行号" 为例,过滤器需:
- 从生产者获取原始数据(作为消费者);
- 对数据进行处理(加行号);
- 将处理后的数据传递给消费者(作为生产者)。
            
            
              Lua
              
              
            
          
          function filter(prod)
    --返回过滤器的协程:从生产者拿值,加行号传递给消费者
    return coroutine.create(function()
        local line = 1 --行号计数器
        while true do 
            local x = receive(prod) --从生产者获取原始值(消费者角色)
            x = string.format("%5d %s",line,x) --处理:加行号
            send(x)        --将处理后的值传递给消费者(生产者角色)
            line = line + 1 --行号自增
        end
    end)
end串联生产者 - 过滤器 - 消费者
三者通过协程串联,仍由消费者主导执行,流程为:消费者→过滤器→生产者。书中简化调用代码:
lua
            
            
              Lua
              
              
            
          
          -- 串联:生产者→过滤器→消费者,消费者主导
consumer(filter(producer()))执行逻辑:消费者需要值时,唤醒过滤器;过滤器需要值时,唤醒生产者;生产者产生值→过滤器处理→消费者消费,形成完整数据流转。
举个例子理解:
            
            
              Lua
              
              
            
          
          -- 基于《Lua程序设计第二版》9.2节"管道与过滤器"核心逻辑:单生产者-单过滤器-单消费者完整实现
-- 1. 基础通信函数(对应书中receive/send,实现协程间数据交互)
function receive(prod)
    local status, value = coroutine.resume(prod)
    return value
end
function send(x)
    coroutine.yield(x)
end
-- 2. 生产者(协程):生成原始数据(模拟从输入读取文本行,书中9.2节用io.read,此处简化为固定3行原始数据)
function producer()
    return coroutine.create(function()
        local raw_data = {"Lua协程管道示例", "原始数据行1", "原始数据行2"}
        for _, line in ipairs(raw_data) do
            send(line)  -- 传递原始数据并挂起,等待过滤器唤醒
        end
        send(nil)  -- 传递nil标识数据结束
    end)
end
-- 3. 过滤器(协程):中间层数据处理(对应书中"加行号"逻辑,给原始数据添加行号前缀)
function filter(prod)
    return coroutine.create(function()
        local line_num = 1  -- 行号计数器
        while true do
            local raw_line = receive(prod)  -- 从生产者获取原始数据(过滤器扮演"消费者")
            if raw_line == nil then
                send(nil)  -- 向下游传递结束信号
                break
            end
            local processed_line = string.format("[第%d行] %s", line_num, raw_line)  -- 数据处理:加行号
            send(processed_line)  -- 传递处理后的数据给消费者(过滤器扮演"生产者")
            line_num = line_num + 1
        end
    end)
end
-- 4. 消费者(主导流程):从过滤器获取处理后的数据并消费(模拟打印输出,书中9.2节用io.write)
function consumer(prod)
    print("===== 消费者开始消费 =====")
    while true do
        local processed_line = receive(prod)  -- 唤醒过滤器获取处理后的数据
        if processed_line == nil then
            print("===== 消费者结束消费 =====")
            break
        end
        print("消费数据:", processed_line)  -- 消费动作:打印
    end
end
-- 5. 启动流程(串联生产者→过滤器→消费者,符合书中"消费者驱动"逻辑)
consumer(filter(producer()))代码流程图
