Lua 基础之协同程序

来源:互联网 发布:angularjs2 seo问题 编辑:程序博客网 时间:2024/06/15 00:38

协同程序

  • 协同程序类似于多线程中的线程。lua 提供 非对称的协同程序 ,即 lua 提供两个函数来控制协同程序的执行,一个用于挂起执行,另一个用于恢复执行。
  • 协同程序的函数放置在表 coroutine 中,创建时传入一个函数,生成一个 thread 类型的值
  • 协同程序的四种状态:挂起(suspended),运行(running)、死亡(dead),正常(normal)
  • 使用 status 函数来查看协程的状态,协程创建时的状态是 挂起
  • 使用 resume 函数可以唤醒协程,将其状态由 挂起 变为 运行
  • 如果协程执行一次就完了,那它的状态就会变成 死亡
  • 使用 yield 函数来挂起协程,将其状态由 运行 变为 挂起
  • 当协程 A 唤醒另一个协程 B 时,A 就处于一个特殊的状态,称为 正常
  • 协程的挂起操作发生在 yield 函数中,也就是说协程运行到 yield 函数时暂停,没有从 yield 函数返回,当下次执行 resume 的时候才从 yield 返回,然后继续执行,直到遇到下一次 yield 或协程运行结束
local mod = ...local m = {}_G[mod] = mpackage.loaded[mod] = mm.main = function()    while coroutine.status(m.co) ~= [[dead]] do        io.read()        coroutine.resume(m.co)    endendm.co = coroutine.create(    function()        for i = 1, 10 do            print("co", i)            coroutine.yield()        end    end)

使用 resume-yield 交换数据

  • 第一次唤醒协程,resume 参数传给协程主函数
coroutine.resume(m.co2, 1, 2, 3)coroutine.resume(m.co2, 10, 20, 30)m.co2 = coroutine.create(    function(a, b, c)        while true do            print("co", a, b, c)            coroutine.yield()        end    end)

co 1 2 3
co 1 2 3

从输出结果可以看出,第一次调用 resume 时,参数的值传给了 a,b,c,第二次调用时则不会

  • yield 传入的参数值传给 resume
print(coroutine.resume(m.co2, 1, 2, 3))print(coroutine.resume(m.co2, 10, 20, 30))m.co2 = coroutine.create(    function(a, b, c)        while true do            print("co", a, b, c)            coroutine.yield(a + b, a + c, b + c)        end    end)

co 1 2 3
true 3 4 5
co 1 2 3
true 3 4 5

  • yield 的返回值是 resume 传入的参数
print(coroutine.resume(m.co2, 1, 2, 3))print(coroutine.resume(m.co2, 10, 20, 30))m.co2 = coroutine.create(    function(a, b, c)        while true do            print("co", coroutine.yield())        end    end)

true
co 10 20 30
true

第一次调用 resume 时,传入的参数 1,2,3 被参数 a,b,c 接收了,然后协程在 yield 函数中挂起,第二次调用 resume,传入的 10,20,30 作为 yield 的返回值,yield 返回到协程继续执行,输出 co 10 20 30

  • 协程运行完毕,主函数返回值传给 resume
print(coroutine.resume(m.co2, 1, 2, 3))print(coroutine.resume(m.co2, 10, 20, 30))m.co2 = coroutine.create(    function(a, b, c)        coroutine.yield()        return a + b + c    end)

true
true 6

总结

  1. resume 第一次执行,参数传给协程主函数
  2. resume 再次执行,参数作为 yield 的返回值
  3. 协程挂起时 yield 的参数作为 resume 的返回值
  4. 协程死亡时主函数的返回值作为 resume 的返回值

生产者-消费者

生产者-消费者是一个经典的协同程序,生产者不停地生产值,然后发送给消费者;消费者不停地接收值,然后催促生产者生产;两者都有自己的主循环,都把自己当作调用的主动方,但实际程序只能有一方是主动方,而另一方则必须由主动方来唤醒,执行一次之后挂起,下面就是一个 消费者驱动 的例子

-- 生产者设计成协程m.producer = coroutine.create(    function()        while true do            local value = tonumber(io.read())            if value == -1 then                break            end            -- 生产者生产值之后挂起            coroutine.yield(value)        end    end)-- 消费者驱动m.customer = function()    while true do        -- 消费者唤醒生产者        local status, value = coroutine.resume(m.producer)        if status == false or coroutine.status(m.producer) == "dead" then            break        end        io.write(value, "\n")    endend

可以在生产者和消费者之前加一个过滤器。生产者是一个协程,过滤器也是一个协程;然后消费者唤醒过滤器,过滤器再唤醒生产者,生产者产生值之后就挂起自己,过滤器接收生产的值后也挂起自己,最后消费者接收生产的值后继续唤醒过滤器

-- 生产者设计成协程m.producer = function()    return coroutine.create(        function()            while true do                local value = io.read()                if value == "-1" then                    break                end                -- 生产者生产值之后挂起                coroutine.yield(value)            end        end    )end-- 过滤器设计成协程m.filter = function(producer)    return coroutine.create(        function()            for line = 1, math.huge do                -- 唤醒生产者                local status, value = coroutine.resume(producer)                -- 生产者死亡                if status == false or coroutine.status(producer) == "dead" then                    break                end                x = string.format("%-5d %s", line, value)                -- 过滤器得到生产值之后挂起                coroutine.yield(x)            end        end    )end-- 消费者驱动m.customer = function(filter)    while true do        -- 唤醒过滤器        local status, value = coroutine.resume(filter)        -- 过滤器死亡        if status == false or coroutine.status(filter) == "dead" then            break        end        io.write(value, "\n")    endend