0Pricing
Lua Academy · Урок

Состояние сопрограммы

Проверяйте состояние сопрограммы с помощью coroutine.status: running, suspended, dead.

«Состояние сопрограммы» — бесплатный урок Lua Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Lua Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Lua Academy содержит 4 уроков всего.

Четыре состояния корутины

У корутин есть четыре возможных состояния: приостановлена (создана или выполнила yield и ожидает запуска), выполняется (в данный момент исполняется), обычная (приостановлена, чтобы дать выполняться другой корутине) и завершена (выполнение окончено или произошла ошибка). Получить состояние можно с помощью coroutine.status(co).

local co = coroutine.create(function()
  print(coroutine.status(coroutine.running()))  -- running
  coroutine.yield()
end)

print(coroutine.status(co))  -- suspended
coroutine.resume(co)          -- prints: running
print(coroutine.status(co))  -- suspended
coroutine.resume(co)
print(coroutine.status(co))  -- dead

Состояние «обычная»

Корутина переходит в состояние «обычная», когда возобновляет другую корутину. Она не выполняется (выполняется другая корутина), но и не приостановлена — она ожидает, пока возобновлённая корутина выполнит yield. Это состояние встречается редко, но его важно понимать при работе со сложными графами корутин.

local inner, outer

inner = coroutine.create(function()
  print("outer status:", coroutine.status(outer))  -- normal
  coroutine.yield()
end)

outer = coroutine.create(function()
  coroutine.resume(inner)
end)

coroutine.resume(outer)
-- prints: outer status: normal

Проверка перед возобновлением

Всегда проверяйте состояние корутины перед возобновлением, особенно в циклах. Возобновление завершённой корутины вызывает ошибку. Возобновление выполняющейся корутины также вызывает ошибку (это привело бы к бесконечной рекурсии).

local function safeResume(co, ...)
  local status = coroutine.status(co)
  if status == "dead" then
    return false, "cannot resume dead coroutine"
  end
  if status == "running" then
    return false, "cannot resume running coroutine"
  end
  return coroutine.resume(co, ...)
end

Состояние после ошибки

Если в функции корутины возникает неперехваченная ошибка, coroutine.resume возвращает false и сообщение об ошибке. Корутина переходит в состояние «завершена». Перезапустить её нельзя — для повторной попытки необходимо создать новую корутину.

local co = coroutine.create(function()
  error("something went wrong")
end)

local ok, err = coroutine.resume(co)
print(ok, err)                      -- false  ...: something went wrong
print(coroutine.status(co))         -- dead

-- Cannot resume dead coroutine:
local ok2, err2 = coroutine.resume(co)
print(ok2, err2)  -- false  cannot resume dead coroutine

coroutine.isyieldable

coroutine.isyieldable() возвращает true, если выполняющаяся корутина может выполнить yield. В главном потоке и в функциях C, не поддерживающих приостановку, возвращается false. Если контекст выполнения неизвестен, всегда проверяйте возможность приостановки перед yield.

local co = coroutine.create(function()
  print("isyieldable:", coroutine.isyieldable())  -- true
  coroutine.yield()
end)

coroutine.resume(co)

-- In main thread:
print("isyieldable:", coroutine.isyieldable())  -- false
-- coroutine.yield()  -- ERROR in main thread

Отслеживание набора корутин

Отслеживайте пул корутин, удаляя завершённые корутины после каждого цикла возобновлений. Это основа планировщика задач.

local function makeWorker(n, name)
  return coroutine.create(function()
    for i = 1, n do
      print(name, "step", i)
      coroutine.yield()
    end
  end)
end

local workers = {
  makeWorker(2, "fast"),
  makeWorker(4, "slow"),
}

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

Отладка корутин

Используйте проверки состояния для инструментирования и отладки кода на основе корутин. Выводите переходы между состояниями, чтобы понимать ход выполнения. В сложных системах присваивайте корутинам имена через таблицу-обёртку, чтобы улучшить отладочный вывод.

local function namedCo(name, fn)
  local co = coroutine.create(fn)
  return {
    co = co,
    name = name,
    resume = function(self, ...)
      print("[" .. self.name .. "] resume")
      local results = table.pack(coroutine.resume(self.co, ...))
      print("[" .. self.name .. "] status: " .. coroutine.status(self.co))
      return table.unpack(results, 1, results.n)
    end
  }
end

local worker = namedCo("worker1", function()
  coroutine.yield()
end)
worker:resume()
worker:resume()

Перезапуск завершённых корутин

После завершения корутину нельзя возобновить. Чтобы её «перезапустить», необходимо создать новую корутину из той же функции. Распространённый шаблон — фабрика, создающая новую корутину по требованию.

local function makeCounter(start, step)
  return coroutine.wrap(function()
    local n = start or 0
    while true do
      coroutine.yield(n)
      n = n + (step or 1)
    end
  end)
end

local count = makeCounter(0, 2)
for i = 1, 5 do io.write(count() .. " ") end
print()  -- 0 2 4 6 8

-- "Restart" by creating a new one
count = makeCounter(100)
print(count())   -- 100

Поведение Coroutine.wrap при ошибках

Если обёрнутая корутина завершается ошибкой, ошибка передаётся наружу через вызов функции-обёртки. В отличие от coroutine.resume, который возвращает false и ошибку, функции, возвращённые через wrap, непосредственно вызывают ошибку. Если необходимо перехватывать ошибки, используйте pcall вокруг вызовов wrap.

local gen = coroutine.wrap(function()
  coroutine.yield(1)
  error("oops")
  coroutine.yield(2)  -- never reached
end)

print(gen())   -- 1 (ok)
local ok, err = pcall(gen)
print(ok, err) -- false  ...: oops
-- Subsequent call: dead coroutine
ok, err = pcall(gen)
print(ok, err) -- false  cannot resume dead coroutine

isyieldable и status

Есть два способа получить сведения о контексте корутины: coroutine.status(co) сообщает состояние определённой корутины, а coroutine.isyieldable() показывает, может ли текущий контекст выполнения выполнить yield. Используйте status для проверки других корутин, а isyieldable — чтобы проверить безопасность собственного yield.

local function maybeYield()
  if coroutine.isyieldable() then
    coroutine.yield()
  else
    -- in main thread or non-yieldable context
    print("cannot yield here")
  end
end

maybeYield()  -- prints "cannot yield here" (main thread)

local co = coroutine.create(maybeYield)
coroutine.resume(co)   -- yields successfully

Быстрая проверка

В какое состояние переходит корутина после завершения функции, лежащей в её основе?

Итоги: состояние корутины

Краткое содержание:

  • Состояния: приостановлена → выполняется ↔ обычная → завершена
  • coroutine.status(co) возвращает строку состояния
  • Завершена = выполнение окончено или произошла ошибка; возобновить нельзя
  • coroutine.isyieldable() проверяет, может ли текущий контекст выполнить yield
  • Всегда проверяйте состояние перед возобновлением в циклах
  • Ошибки wrap передаются напрямую; resume возвращает false+err

Часто задаваемые вопросы

Урок «Состояние сопрограммы» бесплатный?

Да — полный текст урока «Состояние сопрограммы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.

Чему я научусь в уроке «Состояние сопрограммы»?

Проверяйте состояние сопрограммы с помощью coroutine.status: running, suspended, dead. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Lua Academy?

Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Состояние сопрограммы»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Lua Academy?

Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Создание сопрограмм
  2. resume и yield
  3. Состояние сопрограммы
  4. Шаблон «производитель—потребитель»
← Назад к Lua Academy