Lua Coroutine详解

作者: chiguozi | 来源:发表于2016-08-27 22:10 被阅读1989次

    协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈,局部变量和指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。线程与协同程序的主要区别在于,一个具有多线程的程序可以同时运行几个线程,而协同程序却需要彼此协作地运行。就是说,一个具有多个协同程序的程序在任何时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显示地挂起时,它的执行才会暂停。

    一、 lua协程函数概览

    方法 描述
    coroutine.create() 创建coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用
    coroutine.resume() 重启coroutine,和create配合使用
    coroutine.yield() 挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有很多有用的效果
    coroutine.status() 查看coroutine的状态.注:coroutine的状态有四种:dead,suspend,running,normal
    coroutine.wrap() 创建coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine,和create功能重复
    coroutine.running() 返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号

    二、 函数详解

    1. coroutine.create(f)

    创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回这个新协程,它是一个类型为 "thread" 的对象。不会启动该协程。

    local co = coroutine.create(
        function()
            print("in coroutine")
            return "coroutine return"
        end)
    print(co)
    print(coroutine.resume(co))
    
    Output:
    thread: 0039B808
    in coroutine
    true    coroutine return
    

    2. coroutine.resume(co, [, val1, ...])

    开始或继续协程co的运行。当第一次执行一个协程时,他会从主函数处开始运行。val1,...这些值会以参数形式传入主体函数。 如果该协程被挂起,resume 会重新启动它; val1, ... 这些参数会作为挂起点的返回值。如果协程运行起来没有错误, resume 返回 true 加上传给 yield 的所有值 (当协程挂起), 或是主体函数的所有返回值(当协程中止)。

    local co = coroutine.create(
        function (input)
            print("input : "..input)
            local param1, param2 = coroutine.yield("yield")
            print("param1 is : "..param1)
            print("param2 is : "..param2)
            -- return 也会将结果返回给 resume
            return "return"
        end)
    
    --第一次执行,将参数传给input
    print(coroutine.resume(co, "function input"))
    print("this is main chunk")
    --第二次执行,将参数作为yield的返回值,传给param1 param2
    print(coroutine.resume(co, "param1", "param2"))
    
    Output:
    input : function input
    true    yield
    this is main chunk
    param1 is : param1
    param2 is : param2
    true    return
    

    coroutine.resume 是在保护模式中运行,如果有任何错误发生, Lua 是不会显示任何错误, 而是 返回 false 加错误消息。同时,这个协程的状态会变成dead。

    local co = coroutine.create(
        function()
            print("error test")
            --调用一个空值得属性
            coroutine.yield(a.a)
        end)
    -- 返回结果为 false 以及错误信息
    print(coroutine.resume(co))
    -- 协程的状态变为 dead
    print(coroutine.status(co))
    
    Output:
    error test
    false   K:\lua\coroutine.lua:49: attempt to index global 'a' (a nil value)
    dead
    

    3. coroutine.yield(...)

    挂起正在调用的协程的执行。 传递给 yield 的参数都会转为 resume 的额外返回值。

    local co = coroutine.create(
        function ()
            coroutine.yield("yield1" ,"yield2")
        end)
    
    print(coroutine.resume(co))
    
    Output:
    true    yield1  yield2
    

    4. coroutine.status(co)

    以字符串形式返回协程 co 的状态:

    • 当协程正在运行(它就是调用 status 的那个) ,返回 "running";
    • 如果协程调用 yield 挂起或是还没有开始运行,返回 "suspended";
    • 如果协程是活动的,都并不在运行(即它正在延续其它协程),返回 "normal";
    • 如果协程运行完主体函数或因错误停止,返回 "dead"。
    local co
    local co2 = coroutine.create(function() print("3."..coroutine.status(co)) end)
    co = coroutine.create(
        function ()
            print("2."..coroutine.status(co))
            coroutine.resume(co2)
            coroutine.yield()
        end)
    
    print("1."..coroutine.status(co))
    coroutine.resume(co)
    print("4."..coroutine.status(co))
    coroutine.resume(co)
    print("5."..coroutine.status(co))
    
    Output:
    1.suspended
    2.running
    3.normal
    4.suspended
    5.dead
    

    5. coroutine.running()

    返回当前的协程,如果实在主线程,则返回nil

    local co = coroutine.create(
        function () 
            print(coroutine.running()) 
            end)
    
    print(coroutine.running())
    coroutine.resume(co)
    print(co)
    
    Output:
    nil
    thread: 003FC9A0
    thread: 003FC9A0
    

    ** Lua 5.3 有变动 **

    6. coroutine.wrap(f)

    创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回一个函数, 每次调用该函数都会延续该协程。 传给这个函数的参数都会作为 resume 的额外参数。 和 resume 返回相同的值, 只是没有第一个布尔量。

    local wrap = coroutine.wrap(
        function (input)
            print("input : "..input)
            local param1, param2 = coroutine.yield("yield")
            print("param1 is : "..param1)
            print("param2 is : "..param2)
            -- return 也会将结果返回给 resume
            return "return"
        end)
    
    --第一次执行,将参数传给input
    print(wrap("function input"))
    print("this is main chunk")
    --第二次执行,将参数作为yield的返回值,传给param1 param2
    print(wrap("param1", "param2"))
    
    Output:
    input : function input
    yield
    this is main chunk
    param1 is : param1
    param2 is : param2
    return
    

    coroutine.wrap不是保护模式运行,如果发生任何错误,抛出这个错误。

    local wrap = coroutine.wrap(
        function()
            print("error test")
            --调用一个空值得属性
            coroutine.yield(a.a)
        end)
    -- 返回结果为 false 以及错误信息
    print(coroutine.resume(co))
    

    这段代码在运行时,会抛出异常。

    三、实例

    摘自 《Lua 程序设计(第二版)》

    -- 使用协程实现生产者消费者
    function receive()
        local status, value = coroutine.resume(producer)
        return value
    end
    
    function send(x)
        coroutine.yield(x)
    end
    
    producer = coroutine.create(
        function()
            while true do
                local x = io.read()
                send(x)
            end
        end)
    
    function consumer ()
        while true do
            local x = receive()
            io.write(x, "\n")
        end
    end
    consumer();
    

    相关文章

      网友评论

        本文标题:Lua Coroutine详解

        本文链接:https://www.haomeiwen.com/subject/rwstettx.html