2.6 协程

Lua 支持协程,也叫 协同式多线程。一个协程在 Lua 中代表了一段独立的执行线程。然而,与多线程系统中的线程的区别在于,协程仅在显式调用一个让出(yield)函数时才挂起当前的执行。

调用函数coroutine.create可创建一个协程。其唯一的参数是该协程的主函数。create 函数只负责新建一个协程并返回其句柄(一个 thread 类型的对象);而不会启动该协程。

调用coroutine.resume函数执行一个协程。第一次调用coroutine.resume时,第一个参数应传入coroutine.create返回的线程对象,然后协程从其主函数的第一行开始执行。传递给coroutine.resume的其他参数将作为协程主函数的参数传入。协程启动之后,将一直运行到它终止或 让出

协程的运行可能被两种方式终止:正常途径是主函数返回(显式返回或运行完最后一条指令);非正常途径是发生了一个未被捕获的错误。对于正常结束,coroutine.resume将返回 true,并接上协程主函数的返回值。当错误发生时,coroutine.resume 将返回 false与错误消息。

通过调用coroutine.yield使协程暂停执行,让出执行权。协程让出时,对应的最近 coroutine.resume 函数会立刻返回,即使该让出操作发生在内嵌函数调用中(即不在主函数,但在主函数直接或间接调用的函数内部)。在协程让出的情况下,coroutine.resume 也会返回 true,并加上传给coroutine.yield 的参数。当下次重启同一个协程时,协程会接着从让出点继续执行。此时,此前让出点处对 coroutine.yield 的调用会返回,返回值为传给coroutine.resume的第一个参数之外的其他参数。

coroutine.create 类似,coroutine.wrap 函数也会创建一个协程。不同之处在于,它不返回协程本身,而是返回一个函数。调用这个函数将启动该协程。传递给该函数的任何参数均当作 coroutine.resume的额外参数。coroutine.wrap返回coroutine.resume的所有返回值,除了第一个返回值(布尔型的错误码)。和 coroutine.resume不同,coroutine.wrap不会捕获错误;而是将任何错误都传播给调用者。

下面的代码展示了一个协程工作的范例:

  1. function foo (a)
  2. print("foo", a)
  3. return coroutine.yield(2*a)
  4. end
  5.  
  6. co = coroutine.create(function (a,b)
  7. print("co-body", a, b)
  8. local r = foo(a+1)
  9. print("co-body", r)
  10. local r, s = coroutine.yield(a+b, a-b)
  11. print("co-body", r, s)
  12. return b, "end"
  13. end)
  14.  
  15. print("main", coroutine.resume(co, 1, 10))
  16. print("main", coroutine.resume(co, "r"))
  17. print("main", coroutine.resume(co, "x", "y"))
  18. print("main", coroutine.resume(co, "x", "y"))

当你运行它,将产生下列输出:

  1. co-body 1 10
  2. foo 2
  3. main true 4
  4. co-body r
  5. main true 11 -9
  6. co-body x y
  7. main true 10 end
  8. main false cannot resume dead coroutine

你也可以通过 C API 来创建及操作协程:参见函数lua_newthreadlua_resume,以及 lua_yield