Lua コルーチン



コルーチンとは何ですか?

Lua コルーチンはスレッドに似ています。独立したスタック、独立したローカル変数、独立した命令ポインターを持ち、グローバル変数やその他のほとんどのものを他のコルーチンと共有します。

コラボレーションは非常に強力な機能ですが、使い方が非常に複雑でもあります。

スレッドとコルーチンの違い

スレッドとコルーチンの主な違いは、複数のスレッドを持つプログラムは複数のスレッドを同時に実行できるのに対し、コルーチンは相互に連携して実行する必要があることです。

常に 1 つのコルーチンのみが実行され、実行中のコルーチンは明示的に一時停止するように要求された場合にのみ一時停止されます。

コルーチンは同期されたマルチスレッドに似ており、同じスレッド ロックを待機する複数のスレッドはコラボレーションに似ています。

基本構文

と組み合わせて使用​​すると、多くの便利な効果が得られます。 coroutine.running()runningを使用する場合、コルーチンはスレッドです。 、コルーティング スレッド番号を返します

以下の例は、上記の各メソッドの使用法を示しています:

-- 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 コルーチンの基礎となる実装がスレッドであることがわかります。

コルーチンを作成すると、新しいスレッドにイベントが登録されます。

resume を使用してイベントをトリガーすると、create のコルーチン関数が実行されます。yield が発生すると、現在のスレッドが一時停止され、イベントが再度トリガーされるのを待機することになります。

次に、より詳細な例を分析します:

function foo (a)
    print("foo 函数输出", a)
    return coroutine.yield(2 * a) -- 返回  2*a 的值
end
 
co = coroutine.create(function (a , b)
    print("第一次协同程序执行输出", a, b) -- co-body 1 10
    local r = foo(a + 1)
     
    print("第二次协同程序执行输出", r)
    local r, s = coroutine.yield(a + b, a - b)  -- a,b的值为第一次调用协同程序时传入
     
    print("第三次协同程序执行输出", r, s)
    return b, "结束协同程序"                   -- b的值为第二次调用协同程序时传入
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	10
foo 函数输出	2
main	true	4
--分割线----
第二次协同程序执行输出	r
main	true	11	-9
---分割线---
第三次协同程序执行输出	x	y
main	true	10	结束协同程序
---分割线---
main	false	cannot resume dead coroutine
---分割线---

上記の例は次のように続きます:

  • resume を呼び出してコルーチンを起動し、再開操作が返されます。成功した場合は true、そうでない場合は false を返します。

  • コルーチンが実行され、最初の再開が返されます。は再開のパラメータです)

  • 2 回目に再開し、再びコルーチンを起動します (注: ここでの再開パラメータのうち、最初のパラメータを除く残りのパラメータは、yield パラメータとして使用されます)

  • yield returns;

  • コルーチンは実行を継続します

  • 使用されたコルーチンが実行を継続し、完了後に再開メソッドを呼び出し続ける場合、出力は次のようになります: 停止したコルーチンを再開できません

  • resume と yield は、レジュームがメイン プロセス内にあり、外部状態 (データ) をコルーチンに渡し、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
……

メソッド説明
coroutine.create()コルーチンを作成し、コルーチンを返します。パラメータは関数であり、再開と一緒に使用すると、関数呼び出しを起動します
coroutine.resume() コルーチンを再起動し、create
coroutine.yield() で使用します コルーチンを一時停止し、コルーチンを一時停止状態に設定します。これは、resume
coroutine.status()コルーチンの状態を確認します
注: コルーチンにはデッド、サスペンド、実行中の3つの状態があります。このような状態が発生した場合については、以下のプログラムを参照してください
coroutine.wrap( ) コルーチンを作成して関数を返す この関数を呼び出したら、create関数を繰り返してコルーチンに入ります