2.6 コルーチン

Lua はコルーチン (coroutine) をサポートします。コルーチンは協調的マルチスレッディング (collaborative multithreading) とも呼ばれます。Lua のコルーチンは独立した実行の流れを表しますが、マルチスレッドシステムのスレッドとは異なり、コルーチンの実行は yield 関数を明示的に呼ぶまで停止しません。

コルーチンは coroutine.create で作成します。この関数の唯一の引数は作成されるコルーチンのメイン関数です。create 関数は新しいコルーチンを作成してそのハンドル (スレッド型のオブジェクト) を返すだけであり、コルーチンの実行は開始しません。

コルーチンの実行は coroutine.resume で行います。第一引数は coroutine.create が返すスレッドです。初めて coroutine.resume を呼ぶと、コルーチンはメイン関数を呼んで実行を開始します。coroutine.resume に渡されるスレッド以降の引数はメイン関数への引数となります。開始されたコルーチンの実行は終了または yield するまで続きます。

コルーチンの実行終了は二種類あります。メイン関数が返った (最後の命令の実行により明示的または暗黙に実行が値を返した) ときの通常終了と、予期しないエラーが起きたときの異常終了です。coroutine.resume は通常終了のとき true とコルーチンのメイン関数が返した値を返し、異常終了のとき false とエラーオブジェクトを返します。後者の場合コルーチンはスタックを巻き戻さないので、デバッグ API を使って後からエラーを調べることができます。

コルーチンが coroutine.yield を呼ぶと yield が起こります。コルーチンが yield すると対応する coroutine.resume はすぐに値を返し、ネストされた関数 (メイン関数でない、直接あるいは間接的に呼ばれた関数) で yield が起きたとしても同様の処理が起こります。yield に対応する coroutine.resumetrue と yield に渡された値を返します。yield したコルーチンを再度 resume すると、yield を実行したところから実行が再開されます。そのとき coroutine.yield の返り値は coroutine.resume に渡された追加の引数となります。

coroutine.wrapcoroutine.create と同様コルーチンを作成しますが、この関数が返すのはコルーチンではなく「呼ばれたときにコルーチンを resume する関数」です。返り値の関数に渡される引数は全て coroutine.resume の追加の引数となります。coroutine.wrap の返り値は coroutine.resume と同様ですが、一つ目の返り値 (真偽値型のエラーコード) がありません。coroutine.wrap で作られる関数は coroutine.resume と異なりエラーが起こるとコルーチンをクローズ (coroutine.close) し、エラーを呼び出し元に伝播させます。

コルーチンの動作を表すコード例を次に示します:

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_newthread, lua_resume, lua_yield といった関数を参照してください。