Создание сопрограмм
Создавайте сопрограммы с помощью 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) -- resumedcoroutine.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 — локальная установка не требуется.
Все уроки этого курса
- Создание сопрограмм
- resume и yield
- Состояние сопрограммы
- Шаблон «производитель—потребитель»