Lua协程使用

resume和yeild的协作

resume 和 yeild 的协作是 Lua 协程的核心。我们使用一幅图描述一下,有一个大体的印象。对照下面的 coroutine 库的详细解释和最后的代码,应该可以搞清楚协程的概念了。

注:这是在非首次 resume 协程的情况下,resume 和 yield 的互相调用的情况。如果是首次 resume 协程,那么 resume 的参数会直接传递给协程函数。

01_Lua协程使用.png

Lua coroutine库详解

  1. coroutine.create (f):传一个函数参数,用来创建协程。返回一个 “thread” 对象。

  2. coroutine.isyieldable():如果正在运行的协程可以让出,则返回真。值得注意的是,只有主协程(线程)和 C 函数中是无法让出的。

  3. coroutine.resume(co [, val1, ···]):这是一个非常重要的函数。用来启动或再次启动一个协程,使其由挂起状态变成运行状态。

    可以这么说,resume 函数相当于在执行协程中的方法。参数 Val1… 是执行协程 co 时传递给协程的方法。首次执行协程 co 时,参数 Val1… 会传递给协程 co 的函数。

    再次执行协程 co 时,参数 Val1… 会作为给协程 co 中上一次 yeild 的返回值。resume 函数返回什么呢?有 3 种情况:

    • 如果协程 co 的函数执行完毕,协程正常终止,resume 返回 true 和函数的返回值。
    • 如果协程 co 的函数执行过程中,协程让出了(调用了 yeild() 方法),那么 resume 返回 true 和协程中调用 yeild 传入的参数。
    • 如果协程 co 的函数执行过程中发生错误,resume 返回 false 与错误消息。

可以看到 resume 无论如何都不会导致程序崩溃。它是在保护模式下执行的。

  1. coroutine.running():用来判断当前执行的协程是不是主线程,如果是,就返回 true。

  2. coroutine.status(co):返回一个字符串,表示协程的状态。有 4 种状态:

    状态 描述
    running 如果在协程的函数中调用 status,传入协程自身的句柄,那么执行到这里的时候才会返回 running 状态。
    suspended 如果协程还未结束,即自身调用了 yeild 或还没开始运行,那么就是 suspended 状态。
    normal 如果协程 A resume 协程 B 时,协程 A 处于的状态为 normal。在协程 B 的执行过程中,协程A 就一直处于 normal 状态。因为它这时候既不是挂起状态、也不是运行状态。
    dead 如果一个协程发生错误结束,或正常终止。那么就处于 dead 状态。如果这时候对它调用 resume,将返回 false 和错误消息。
  3. coroutine.wrap(f):wrap() 也是用来创建协程的。只不过这个协程的句柄是隐藏的。跟 create() 的区别在于:

    • wrap() 返回的是一个函数,每次调用这个函数相当于调用 coroutine.resume()。
    • 调用这个函数相当于在执行 resume() 函数。
    • 调用这个函数时传入的参数,就相当于在调用 resume 时传入的除协程的句柄外的其他参数。
    • 调用这个函数时,跟 resume 不同的是,它并不是在保护模式下执行的,若执行崩溃会直接向外抛出。
  4. coroutine.yield (···):使正在执行的函数挂起。

    传递给 yeild 的参数会作为 resume 的额外返回值。同时,如果对该协程不是第一次执行 resume,resume 函数传入的参数将会作为 yield 的返回值。

案例

resume yield使用

简单使用 resume yield 函数

#!/usr/bin/lua print("haicoder(www.haicoder.net)\n") coco = coroutine.create(function (a,b) print("resume args:"..a..","..b) yreturn = coroutine.yield() print ("yreturn :"..yreturn) end) coroutine.resume(coco, 0, 1) coroutine.resume(coco, 21)

程序运行后,控制台输出如下:

02_Lua协程使用.png

yield 使正在执行的函数挂起。

wrap使用

简单使用 wrap 函数

#!/usr/bin/lua print("haicoder(www.haicoder.net)\n") coco2 = coroutine.wrap(function (a,b) print("resume args:"..a..","..b) yreturn = coroutine.yield() print ("yreturn :"..yreturn) end) print(type(coco2)) coco2(0,1) coco2(21)

程序运行后,控制台输出如下:

03_Lua协程使用.png

wrap 用来创建隐藏协程。

生产者消费者问题

模拟生产者消费者问题

#!/usr/bin/lua print("haicoder(www.haicoder.net)\n") local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- 将生产的物品发送给消费者 end end function consumer() while true do local i = receive() -- 从生产者那里得到物品 print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x表示需要发送的值,值返回以后,就挂起该协同程序 end -- 启动程序 newProductor = coroutine.create(productor) consumer()

程序运行后,控制台输出如下:

04_Lua协程使用.png

我们使用了协程模拟了生产者消费者问题。