Корутины в Lua с примерами
Корутины это что-то вроде потоков, но при этом они все равно ими не являются. Поток, это когда (например) на отдельное ядро процессора м ожно повесить полностью отдельную задачу, а корутина это когда блоки кода работают по очереди и могут быть остановлены в любом моменте, чтобы продолжить исполнение другого кода.
Вот несколько наглядных практических примеров использования корутин:
Пример 1. "Сглаживание" нагрузки тяжелых функций
Все функции в Lua выполняются последовательно. Корутины позволяют сделать некую "перемешку" выполняемых задач, что полезно, когда необходимо, например, на каждом игроке выполнить тяжелую функцию, но при этом не на всех сразу, чтобы сгладить процесс. После выполнения функции на одном игроке, корутина позволит выполнить остальную часть кода до следующего "тика". Затем она выполнит функцию на втором игроке, после чего снова выполнится весь остальной код.
Предположим, у нас есть 100 игроков, для каждого из которых нужно "фоново" обновить данные в БД и еще создать на карте монстра.
Без корутин наш код выглядел бы примерно так:
-- эта таблица, как правило, глобальная и в ней может меняться кол-во игроков
local players = {"a", "b", "c", "d", "e", "f", "g"}
for tick = 1, 50 do
for _, pl in ipairs(players) do
update_db(pl) -- для 100 игроков = 100 запросов каждый тик
end
spawn_monster()
end
С таким кодом через 50 тиков при 100 игроках выполнилось бы 5000 запросов и заспавнилось бы 50 монстров. А вот минимальный вариант с корутиной:
-- эта таблица, как правило, глобальная и в ней может меняться кол-во игроков
local players = {"a", "b", "c", "d", "e", "f", "g"}
local function update_players_subthread()
-- Бесконечно идем по таблице игроков
-- Когда закончивает ся, начинаем сначала
-- coroutine.yield не даст скрипту зависнуть из-за while true
local i = 1
while true do
local pl = players[i]
if pl then
i = i + 1
update_db(pl)
coroutine.yield()
else
i = 1
end
end
end
local co_update_players = coroutine.create(update_players_subthread)
for tick = 1, 50 do
coroutine.resume(co_update_players)
spawn_monster()
end
С корутиной реализация сложнее, но при этом она сильно экономит кол-во запросов к БД. Через 50 тиков выполнится 50 запросов и заспавнится 50 монстров.