0Pricing
Lua Academy · レッスン

コルーチンの作成

coroutine.createとcoroutine.wrapでコルーチンを作成します。

「コルーチンの作成」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。

コルーチンとは

コルーチンとは、実行を一時停止(yield)し、後から中断した場所から再開できる関数です。スレッドとは異なり、コルーチンは協調的に動作します。つまり、一度に実行されるのは 1 つだけで、明示的に制御を譲ります。Lua のコルーチンは独自のスタックとローカル変数を持ちます。

local co = coroutine.create(function()
  print("start")
  coroutine.yield()
  print("resumed")
  coroutine.yield()
  print("done")
end)

print(coroutine.status(co))   -- suspended
coroutine.resume(co)          -- start
print(coroutine.status(co))   -- suspended
coroutine.resume(co)          -- resumed

coroutine.create と coroutine.wrap

coroutine.create(fn) はコルーチンを作成し、コルーチンオブジェクト(型は thread)を返します。coroutine.wrap(fn) はコルーチンを作成し、呼び出すとコルーチンを再開する関数を返します。wrap は簡単ですが、コルーチンオブジェクトは隠されます。

-- create: returns coroutine object
local co = coroutine.create(function(x)
  return x * 2
end)
local ok, val = coroutine.resume(co, 5)
print(ok, val)   -- true  10

-- wrap: returns a resumable function
local gen = coroutine.wrap(function(x)
  return x * 3
end)
print(gen(5))    -- 15

コルーチンのライフサイクル

コルーチンには 4 つの状態があります。suspended(実行されていない)、running(現在実行中)、dead(return またはエラーで終了)、normal(別のコルーチンを再開したため一時停止中)です。coroutine.status(co) で状態を確認できます。

local co = coroutine.create(function()
  coroutine.yield()
end)

print(coroutine.status(co))  -- suspended
coroutine.resume(co)          -- runs until yield
print(coroutine.status(co))  -- suspended again
coroutine.resume(co)          -- runs until end
print(coroutine.status(co))  -- dead

複数のコルーチンの実行

複数のコルーチンを作成し、それぞれを順番に再開して処理を交互に進めます。これにより、並行実行(協調的マルチタスク)をシミュレートできます。各コルーチンは yield の間も自身の状態を保持します。

local function worker(name, steps)
  for i = 1, steps do
    print(name .. ": step " .. i)
    coroutine.yield()
  end
end

local co1 = coroutine.create(function() worker("A", 3) end)
local co2 = coroutine.create(function() worker("B", 3) end)

for _ = 1, 3 do
  coroutine.resume(co1)
  coroutine.resume(co2)
end
-- A:1, B:1, A:2, B:2, A:3, B:3

coroutine.wrap による反復

coroutine.wrap はイテレーターの作成によく使われます。返された関数を呼び出すたびに、イテレーターが次へ進みます。コルーチン本体が終了すると、この関数はエラーを発生させます。必要に応じて pcall を使うか、状態を確認してください。

local function range(n)
  return coroutine.wrap(function()
    for i = 1, n do
      coroutine.yield(i)
    end
  end)
end

for v in range(5) do
  io.write(v .. " ")
end
print()  -- 1 2 3 4 5

コルーチン内のエラー

コルーチンでエラーが発生すると、coroutine.resume は false に続けてエラーメッセージを返します。コルーチンは dead 状態に移行し、終了したコルーチンを再び再開することはできません。

local co = coroutine.create(function(x)
  if x < 0 then error("negative input: " .. x) end
  return math.sqrt(x)
end)

local ok, val = coroutine.resume(co, -1)
print(ok, val)    -- false  input:2: negative input: -1
print(coroutine.status(co))  -- dead

-- Can't resume a dead coroutine:
ok, val = coroutine.resume(co)
print(ok, val)    -- false  cannot resume dead coroutine

コルーチンの識別

coroutine.running() は、現在実行中のコルーチンと、それがメインスレッドかどうかを示すブール値を返します。コルーチン内でこれを使うと、自身への参照を取得できます。

local co = coroutine.create(function()
  local self, isMain = coroutine.running()
  print("type:", type(self))      -- thread
  print("isMain:", isMain)        -- false
  print("status:", coroutine.status(self)) -- running
end)

coroutine.resume(co)

-- In main thread:
local main, isMain = coroutine.running()
print("main isMain:", isMain)    -- true

無限ジェネレーター

コルーチン本体に yield を含む無限ループを記述できます。これにより、ジェネレーターは終了せず、要求に応じて値を生成します。これは、遅延評価される無限シーケンスという重要な用途です。

local function naturals(start)
  return coroutine.wrap(function()
    local n = start or 1
    while true do
      coroutine.yield(n)
      n = n + 1
    end
  end)
end

local gen = naturals(1)
for i = 1, 8 do
  io.write(gen() .. " ")
end
print()  -- 1 2 3 4 5 6 7 8

タスクとしてのコルーチン

基本的なタスクスケジューラーでは、コルーチンをキューに保存し、それぞれを順番に再開します。コルーチンが yield するとキューに戻し、終了すると削除します。これは最小限の協調型スケジューラーです。

local tasks = {}

local function spawn(fn)
  tasks[#tasks+1] = coroutine.create(fn)
end

local function run()
  while #tasks > 0 do
    local alive = {}
    for _, co in ipairs(tasks) do
      coroutine.resume(co)
      if coroutine.status(co) ~= "dead" then
        alive[#alive+1] = co
      end
    end
    tasks = alive
  end
end

spawn(function() for i=1,3 do print("T1:"..i); coroutine.yield() end end)
spawn(function() for i=1,3 do print("T2:"..i); coroutine.yield() end end)
run()

コルーチンのメモリ

各コルーチンには独自のスタックがあります。深い呼び出しチェーンでは、スタックが大きくなることがあります。高パフォーマンスが求められる場面では、同時に数千のコルーチンを作成して稼働させることは避けてください。可能な場合はコルーチンを再利用するか、プールを使用してください。

-- Coroutine pool concept
local pool = {}

local function getCoroutine(fn)
  -- Reuse dead coroutine slots
  for i, co in ipairs(pool) do
    if coroutine.status(co) == "dead" then
      pool[i] = coroutine.create(fn)
      return pool[i]
    end
  end
  local co = coroutine.create(fn)
  pool[#pool+1] = co
  return co
end

print("Pool pattern for coroutine reuse")

クイックチェック

coroutine.create(fn)の直後、コルーチンはどの状態になりますか?

復習:コルーチンの作成

まとめ:

  • coroutine.create(fn) → コルーチンオブジェクト、wrap(fn) → 再開可能な関数
  • 状態:suspended → running → suspended/dead
  • coroutine.resume(co, ...)で開始または再開
  • resume中のエラー:false + エラーメッセージ、コルーチン → dead
  • while true do yield() endによる無限ジェネレーター
  • 協調的マルチタスクと遅延シーケンスに使用

よくある質問

「コルーチンの作成」レッスンは無料ですか?

はい。「コルーチンの作成」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。

「コルーチンの作成」で何を学びますか?

coroutine.createとcoroutine.wrapでコルーチンを作成します。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Lua Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「コルーチンの作成」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLua Academyレッスンでコードを書いて実行できますか?

はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. コルーチンの作成
  2. resumeとyield
  3. コルーチンの状態
  4. プロデューサー・コンシューマーパターン
← Lua Academyに戻る