English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Luaのコルーチンはスレッドと比較して似ています:独立したスタック、独立したローカル変数、独立したコマンドポインタを持ちつつ、他のコルーチンとともにグローバル変数やほとんどすべてのものを共有しています。
コルーチンは非常に強力な機能ですが、使い方は複雑です。
スレッドとコルーチンの主な違いは、複数のスレッドを持つプログラムが同時に複数のスレッドを実行できるのに対し、コルーチンはお互いに協力して実行する必要があります。
指定された任意の瞬間に、コルーチンが1つだけ実行されます。そして、実行中のコルーチンは、明示的にスレイプ要求されたときだけスレイプされます。
コルーチンは、同期多スレッドに少し似たもので、同じスレッドロックを待つ複数のスレッドはコルーチンに少し似ています。
メソッド | 説明 |
---|---|
coroutine.create() | コルーチンを作成し、コルーチンを返します。引数は関数であり、resumeと組み合わせて使用すると、関数の呼び出しを再開します。 |
coroutine.resume() | コルーチンを再開します。createと組み合わせて使用します。 |
coroutine.yield() | コルーチンをスレイプします。コルーチンをスレイプ状態に設定し、resumeと組み合わせて使用すると、多くの効果があります。 |
coroutine.status() | コルーチンの状態を確認します 注:コルーチンの状態は3種類あります:dead、suspended、running。どのタイミングでこのような状態になるかは以下のプログラムを参照してください。 |
coroutine.wrap() | コルーチンを作成し、関数を返します。この関数を呼び出すと、コルーチンに移行します。create機能と重複しています。 |
coroutine.running() | 現在実行中のコルーチンを返します。コルーチンはスレッドと同じであり、runningを使用すると、コルーチンのスレッドIDを返します。 |
-- coroutine_test.lua ファイル co = coroutine.create( function(i) print(i); end ) coroutine.resume(co, 1) -- 1 print(coroutine.status(co)) -- dead print("----------") co = coroutine.wrap( function(i) print(i); end ) co(1) print("----------") co2 = coroutine.create( function() for i=1,10 do print(i) if i == 3 then print(coroutine.status(co2)) --running print(coroutine.running()) --thread:XXXXXX end coroutine.yield() end end ) coroutine.resume(co2) --1 coroutine.resume(co2) --2 coroutine.resume(co2) --3 print(coroutine.status(co2)) -- suspended print(coroutine.running()) print("----------")
以下の例の実行結果は:
1 dead ---------- 1 ---------- 1 2 3 running thread: 0x7fb801c05868 false suspended thread: 0x7fb801c04c88 true ----------
coroutine.runningを使うと、coroutineが底層でスレッドとして実装されていることがわかります。
coroutineをcreateするとき、新しいスレッドにイベントを登録することになります。
resumeを使用してイベントをトリガーするとき、createのcoroutine関数が実行されます、yieldに遭遇すると現在のスレッドを一時停止し、次のresumeのトリガーを待ちます。
次に、より詳細な例を分析します:
function foo (a) print("foo 関数の出力", a) return coroutine.yield(2 * a) -- 返します 2*a の値 end co = coroutine.create(function (a , b) print("第1回コルーチン実行の結果出力", a, b) -- co-body 1 10 local r = foo(a + 1) print("第2回コルーチン実行の結果出力", r) local r, s = coroutine.yield(a + b, a - b) -- a、bの値は第1回コルーチン呼び出し時に入力されます print("第3回コルーチン実行の結果出力", r, s) return b, "コルーチンを終了します" -- bの値は第2回コルーチン呼び出し時に入力されます end) print("main", coroutine.resume(co, 1, 10)) -- true, 4 print("--分割線----") print("main", coroutine.resume(co, "r")) -- true 11 -9 print("---分割線---") print("main", coroutine.resume(co, "x", "y")) -- true 10 end print("---分割線---") print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine print("---分割線---")
以下の例の実行結果は:
第1回コルーチン実行の結果出力 1 10 foo 関数の出力 2 main true 4 --分割線---- 第2回コルーチン実行の結果出力 r main true 11 -9 ---分割線--- 第3回コルーチン実行の結果出力 x y main true 10 コルーチンを終了します ---分割線--- main false cannot resume dead coroutine ---分割線---
以下の例を続けます:
resumeを呼び出し、コルーチンを唤醒します、resume操作が成功するとtrueが返され、それ以外の場合はfalseが返されます;
コルーチンが実行されます;
yield文まで実行されます;
yieldがコルーチンを一時停止させ、最初のresumeが返されます;(注意:ここでのyieldはresumeの引数を返します)
第二次のresume、コルーチンを再び唤醒します;(注意:ここでのresumeの引数には、最初の引数以外の引数がyieldの引数として使用されます)
yieldが返信されます;
コルーチンが再び実行されます;
使用しているコルーチンが再び実行されるとき、resumeメソッドが呼び出されると、cannot resume dead coroutineとエラーが表示されます。
resumeとyieldの組み合わせが強力なのは、resumeがメインプロセスに位置し、外部の状態(データ)をコルーチン内部に传入し、一方でyieldが内部の状態(データ)をメインプロセスに返すからです。
今、Luaの協同プログラムを使用して、プロデューサー問題を解決します。-消費者問題という古典的な問題です。
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()
以下の例の実行結果は:
1 2 3 4 5 6 7 8 9 10 11 12 13 ……