Справочное руководство по языку Lua 5.1 :: 2.11 - Значения и типы



2.11 - Подпрограммы

Lua поддерживает подпрограммы, эту технологию часто называют общей многопоточностью. Подпрограмма Lua представляет собой независимый поток выполнения. Несмотря на это, в отличие от потоков в традиционных многопоточных системах, подпрограмма может приостановить свое выполнение только в результате явного вызова функции yield.

Подпрограммы создаются вызовом coroutine.create. Единственным аргументом является имя главной функции подпрограммы. Функция create только создает новую подпрограмму и возвращает указатель на нее (объект типа thread), запуск подпрограммы не выполняется.

При вызове функции coroutine.resume и передаче ей в качестве первого аргумента результата вызова coroutine.create, процедура запускается на выполнение с первого оператора ее главной функции. Остальные параметры из вызова coroutine.resume передаются в основную функцию подпрограммы. После запуска подпрограмма выполняется до завершения либо до вызова yields.

Подпрограмма останавливается только в двух случаях: нормально, когда осуществляется возврат (явно или неявно) из главной функции; или аварийно в случае необработанной ошибки. При нормальном завершении coroutine.resume возвращает true плюс любые значения, возвращаемые из основной функции подпрограммы. В случае ошибок, coroutine.resume вернет значение false плюс сообщение об ошибке.

Для приостановки выполнения попрограммы используется функция coroutine.yield. При вызове yields соответствующий coroutine.resume возвращает управление немедленно, точно так же, как если бы вызов yield произошел во вложенном вызове функции (т.е. не в главной функции, а в функции, вызванной непосредственно или опосредованно из нее). При вызове yield функция coroutine.resume также возвращает true плюс все входные параметры, переданные в coroutine.yield. В следующий раз, когда подпрограмма продолжит работу, ее выполнение начнется с оператора, следующего за yield, соответственно из coroutine.yield вернутся параметры, переданные в coroutine.resume.

Функция coroutine.wrap создает подпрограмму и осуществляет ее запуск. Параметры, переданные в нее в качестве дополнительных аргуметров, попадают в неявный вызов coroutine.resume. Вызов coroutine.wrap возвращает те же значения, что и coroutine.resume, за исключением первого (булевского кода ошибки). В отличие от coroutine.resume, coroutine.wrap не перехватывает ошибки – все ошибки попадают на уровень вызывающей функции.

Рассмотрим в качестве примера следующий код:


function foo (a)
       print("foo", a)
       return coroutine.yield(2*a)
     end
   
     co = coroutine.create(function (a,b)
           print("co-body", a, b)
           local r = foo(a+1)
           print("co-body", r)
           local r, s = coroutine.yield(a+b, a-b)
           print("co-body", r, s)
           return b, "end"
     end)
            
     print("main", coroutine.resume(co, 1, 10))

     print("main", coroutine.resume(co, "r"))

     print("main", coroutine.resume(co, "x", "y"))

     print("main", coroutine.resume(co, "x", "y"))

При запуске на экран выведется:


     co-body 1       10     
     foo     2     
     main    true    4
     co-body r
     main    true    11      -9
     co-body x       y
     main    true    10      end
     main    false   cannot resume dead coroutine