Skip to content

2.6 - 协程(Coroutines)

Lua支持协程(Coroutines),其也被称为协同式线程(collaborative multithreading)。Lua中的协程表示一个独立执行线程。然而不同于多线程系统中的线程,协程只能通过显示调用让出函数来挂起自身的执行。

通过调用coroutine.create你可以创建一个协程。此函数的唯一参数是用作协程主函数的方法。create函数只是创建一个新协程并返回其句柄(一个thread类型的对象),并不会启动协程。

你可以调用coroutine.resume来执行协程。当你首次调用coroutine.resume,需要将coroutine.create返回的结果作为第一个参数传递,协程将会通过调用其主函数来启动执行。其余传递给coroutine.resume的参数会被传递到这个函数中。启动协程将会一直运行,直到它结束或让出(yields)

协程可以通过两种方式来结束运行:通常情况下是其主函数返回(显式或隐式,在最后一条指令后);特别情况是产生了未在保护下的错误。正常结束的情况下,coroutine.resume返回true,后跟协程的主函数返回的任意结果值。在发生错误时,coroutine.resume返回false,后边跟着错误对象。这种情况下,协程不会展开堆栈,所以在错误发生后可以通过调试API来调查。

协程通过调用coroutine.yield来让出。当协程让出时,相应的coroutine.resume会立刻返回,即使让出发生在内嵌的函数调用中(即不在主函数中,而是在主函数直接或间接调用的函数中)。当在让出的情况下,coroutine.resume也是返回true,后跟由coroutine.yield传递而来的值。当你下次重启同一个协程时,其将会在之前让出的地方继续执行,调用coroutine.yield的地方会返回由coroutine.resume传递过来的额外参数。

coroutine.create一样,coroutine.wrap函数也可以创建协程,但不返回协程本身,而是返回一个用来启动协程的函数。任何传递到这个函数的参数都会作为coroutine.resume的额外参数传入。coroutine.warp返回所有coroutine.resume的值,除了第一个(那个布尔错误码)。与coroutine.resume不同,由coroutine.wrap创建的函数不会传播任何错误给用户。这种情况下,此函数还会关闭协程(参见coroutine.close)。

作为一个展示协程如何工作的例子,请考量以下代码:

lua
function foo (a)
  print("foo", a)
  return coroutine.yield(2*a)
end

co = coroutine.create(function (a,b)
      print("co-body", a, b)
      local r = foo(a+1)
      print("co-body", r)
      local r, s = coroutine.yield(a+b, a-b)
      print("co-body", r, s)
      return b, "end"
end)

print("main", coroutine.resume(co, 1, 10))
print("main", coroutine.resume(co, "r"))
print("main", coroutine.resume(co, "x", "y"))
print("main", coroutine.resume(co, "x", "y"))

当你运行此代码,将产生以下输出:

co-body 1       10
foo     2
main    true    4
co-body r
main    true    11      -9
co-body x       y
main    true    10      end
main    false   cannot resume dead coroutine

你也可以通过C API来创建和处理协程:参见lua_newthreadlua_resumelua_yield函数。