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.resume
は true と yield に渡された値を返します。yield したコルーチンを再度 resume すると、yield を実行したところから実行が再開されます。そのとき coroutine.yield
の返り値は coroutine.resume
に渡された追加の引数となります。
coroutine.wrap
は coroutine.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
といった関数を参照してください。