Тема: LUA Coroutines Tutorial
Coroutines Tutorial
Что есть ко-рутины?
Ко-рутины или сопрограммы, позволяют нам выполнять несколько задач одновременно. В любом месте и в любое время, мы можем прервать любую функцию (в данном случае, ко-рутину), передать управление другой, а потом вернуться, как ни в чём не бывало в прерванную ко-рутину и продолжать с того места откуда она была прервана. Таким образом, достигается псевдо-многозадачность в рамках LUA-кода одной виртуальной машины.
Multi-Threading
Каждая задача выполняется в потоке, который является отдельным от других потоков. Под многопоточностью (Multi-Threading) часто подразумевают одновременное выполнение нескольких задач.
Есть разные способы, с помощью которых многопоточность может быть реализована. Некоторые системы выделяют определенное количество времени для каждого потока, по прошествии которого передают управление на следующий поток и т.д. Это называется pre-emptive multi-threading (или упреждающая многопоточность). В этом случае каждому из потоков не нужно беспокоиться о том, как много времени он занимает, он больше озабочен своей функцией.
В других системах, наоборот, поток знает сколько он должен выполняться. Это называется cooperative, или collaborative multi-threading. Здесь все потоки сотрудничают вместе, чтобы разрешить приложению работать должным образом. Это тип многозадачности, который используют сопрограммы Lua.
Не стоит путать потоки операционной системы и потоки и сопрограммы (ко-рутины) LUA. Сопрограммы - блоки Lua-кода, которые создаются в Lua, и имеют свой собственный поток управления, также как и потоки ОС. Только одна сопрограмма работает в одно и то же время, пока не будет активирована другая подпрограмма, или пока не будет возврата к той подпрограмме, которая её вызвала. Сопрограммы - способ дать возможность псевдо-одновременного исполнения многим другим сопрограммам удобным и естественным образом, однако, это нельзя назвать чистым параллелизмом, и, таким образом, нет никакого выигрыша в производительности при использовании нескольких процессоров. Однако, поскольку сопрограммы переключаются намного быстрее, чем потоки в операционной системе и обычно не требуют сложных, а иногда и дорогих механизмов блокировки, применение сопрограмм, как правило, быстрее, чем эквивалентные программы, использущие полноценные потоки ОС.
Yielding
Чтобы осуществить вышесказанное несколько сопрограмм должны прекратить выполнение (после выполнения разумного объема обработки) и передать управление другому потоку. Это называется "yielding". Сопрограммы явно должны вызвать функцию Lua coroutine.yield() , которая похожа на использование return-функции. Однако, в отличии от return - мы можем снова вернуться в прерванную функцию и продолжить с того места где мы остановились. При выходе же по return мы безвозвратно теряем все локальные переменные в той функции из которой вышли.
Довольно слов, несколько примеров:
Чтобы создать сопрограмму, мы должны иметь функцию, которая её представляет, например,
> function foo()
>> print("foo", 1)
>> coroutine.yield()
>> print("foo", 2)
>> end
>
Мы создаем подпрограмму с помощью coroutine.create(fn) функции. Мы передаем ей точка входу для потока, который является функцией Lua. Объект, возвращаемый Lua является поток:
> co = coroutine.create(foo) -- create a coroutine with foo as the entry
> = type(co) -- display the type of object "co"
thread
Мы можем узнать, в каком состоянии находится поток, используя для этого функцию coroutine.status(), например,
> = coroutine.status(co)
suspended
Состояние "suspended" означает, что поток жив, и, как и следовало ожидать, ничего не делает. Обратите внимание, что, когда мы создали поток - он НЕ начинает выполнение. Чтобы начать выполнение, мы используем функцию coroutine.resume(). После её вызова Lua будет входить в поток и выходить из него по coroutine.yield().
> = coroutine.resume(co)
foo 1
true
функция coroutine.resume() возвращает статус ошибки после своего вызова. Выше показано, что мы вошли в функцию foo , а затем вышел без ошибок. Интересный момент: с функцией такой бы номер не прошёл, но с сопрограммами мы сможем возобновить выполнение снова:
> = coroutine.resume(co)
foo 2
true
Видно, что мы выполнили строку после выхода в foo и снова вернулся без ошибок. Тем не менее, если мы посмотрим на status мы можем видеть, что мы покинули функцию foo и сопрограмма прекращается.
> = coroutine.status(co)
dead
Если мы попытаемся провернуть этот номер снова - мы получим сообщение об ошибке:
> = coroutine.resume(co)
false cannot resume dead coroutine
После того, как сопрограмма закончила свою работу - она не может быть возобновлена.
Подробнее
Ниже более сложный пример демонстрирует некоторые важные особенности сопрограмм.
> function odd(x)
>> print('A: odd', x)
>> coroutine.yield(x)
>> print('B: odd', x)
>> end
>
> function even(x)
>> print('C: even', x)
>> if x==2 then return x end
>> print('D: even ', x)
>> end
>
> co = coroutine.create(
>> function (x)
>> for i=1,x do
>> if i==3 then coroutine.yield(-1) end
>> if i % 2 == 0 then even(i) else odd(i) end
>> end
>> end)
>
> count = 1
> while coroutine.status(co) ~= 'dead' do
>> print('----', count) ; count = count+1
>> errorfree, value = coroutine.resume(co, 5)
>> print('E: errorfree, value, status', errorfree, value, coroutine.status(co))
>> end
---- 1
A: odd 1
E: errorfree, value, status true 1 suspended
---- 2
B: odd 1
C: even 2
E: errorfree, value, status true -1 suspended
---- 3
A: odd 3
E: errorfree, value, status true 3 suspended
---- 4
B: odd 3
C: even 4
D: even 4
A: odd 5
E: errorfree, value, status true 5 suspended
---- 5
B: odd 5
E: errorfree, value, status true nil dead
>
В основном у нас есть цикл for, который вызывает две функции: odd() , когда он сталкивается с нечетным числом, и even() на четных чисел.
Оригинал на англ.: [url]http://lua-users.org/wiki/CoroutinesTutorial[/url]
Перевод: sam063rus
за что ему большое спасибо! admin