0Pricing
Lua Academy · Урок

Создание сопрограмм

Создавайте сопрограммы с помощью coroutine.create и coroutine.wrap.

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

Что такое сопрограмма?

Сопрограмма — это функция, которая может приостановить своё выполнение (yield), а затем продолжить его с места остановки. В отличие от потоков, сопрограммы работают совместно — одновременно выполняется только одна, и она явно передаёт управление. У сопрограмм 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

Жизненный цикл сопрограммы

У сопрограммы есть четыре состояния: приостановлена (не выполняется), выполняется (в данный момент исполняется), завершена (вернула результат или получила ошибку) и обычная (приостановлена, поскольку возобновила другую сопрограмму). Проверяйте состояние с помощью 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

Параллельное выполнение сопрограмм

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

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

Бесконечный генератор

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

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

Сопрограммы как задачи

Базовый планировщик задач хранит сопрограммы в очереди и по очереди возобновляет каждую. Когда сопрограмма приостанавливается, она возвращается в очередь. Когда она завершается, её удаляют. Это минимальный планировщик совместной многозадачности.

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) → возобновляемая функция
  • Состояния: приостановлена → выполняется → приостановлена/завершена
  • coroutine.resume(co, ...) запускает или возобновляет выполнение
  • Ошибки при возобновлении: false + сообщение об ошибке; корутина → завершена
  • Бесконечные генераторы с помощью while true do yield() end
  • Используйте корутины для кооперативной многозадачности и ленивых последовательностей

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

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

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

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

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

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

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

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

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

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

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

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

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