Lua - Corutinas
Introducción
Las corrutinas son de naturaleza colaborativa, lo que permite que dos o más métodos se ejecuten de manera controlada. Con las corrutinas, en un momento dado, solo se ejecuta una corrutina y esta corrutina en ejecución solo suspende su ejecución cuando solicita explícitamente ser suspendida.
La definición anterior puede parecer vaga. Supongamos que tenemos dos métodos, uno el método del programa principal y una corrutina. Cuando llamamos a una corrutina usando la función resume, comienza a ejecutarse y cuando llamamos a la función yield, suspende la ejecución. Nuevamente, la misma corrutina puede continuar ejecutándose con otra llamada a la función de reanudación desde donde se suspendió. Este proceso puede continuar hasta el final de la ejecución de la corrutina.
Funciones disponibles en corrutinas
La siguiente tabla enumera todas las funciones disponibles para corrutinas en Lua y su uso correspondiente.
No Señor. | Método y propósito |
---|---|
1 | coroutine.create (f) Crea una nueva corrutina con una función f y devuelve un objeto de tipo "hilo". |
2 | coroutine.resume (co [, val1, ...]) Reanuda la co-rutina co y pasa los parámetros si los hay. Devuelve el estado de funcionamiento y otros valores de retorno opcionales. |
3 | coroutine.running () Devuelve la corrutina en ejecución o nil si se llama en el hilo principal. |
4 | coroutine.status (co) Devuelve uno de los valores de ejecución, normal, suspendida o muerta según el estado de la corrutina. |
5 | coroutine.wrap (f) Como coroutine.create, la función coroutine.wrap también crea una corrutina, pero en lugar de devolver la corrutina en sí, devuelve una función que, cuando se llama, reanuda la corrutina. |
6 | coroutine.yield (...) Suspende la corrutina en ejecución. El parámetro pasado a este método actúa como valores de retorno adicionales a la función de reanudación. |
Ejemplo
Veamos un ejemplo para entender el concepto de corrutinas.
co = coroutine.create(function (value1,value2)
local tempvar3 = 10
print("coroutine section 1", value1, value2, tempvar3)
local tempvar1 = coroutine.yield(value1+1,value2+1)
tempvar3 = tempvar3 + value1
print("coroutine section 2",tempvar1 ,tempvar2, tempvar3)
local tempvar1, tempvar2= coroutine.yield(value1+value2, value1-value2)
tempvar3 = tempvar3 + value1
print("coroutine section 3",tempvar1,tempvar2, tempvar3)
return value2, "end"
end)
print("main", coroutine.resume(co, 3, 2))
print("main", coroutine.resume(co, 12,14))
print("main", coroutine.resume(co, 5, 6))
print("main", coroutine.resume(co, 10, 20))
Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado.
coroutine section 1 3 2 10
main true 4 3
coroutine section 2 12 nil 13
main true 5 1
coroutine section 3 5 6 16
main true 2 end
main false cannot resume dead coroutine
¿Qué hace el ejemplo anterior?
Como se mencionó anteriormente, usamos la función de reanudación para iniciar la operación y la función de rendimiento para detener la operación. Además, puede ver que hay varios valores de retorno recibidos por la función de reanudación de la rutina.
Primero, creamos una corrutina y le asignamos un nombre de variable co y la corrutina toma dos variables como parámetros.
Cuando llamamos a la primera función de reanudación, los valores 3 y 2 se retienen en las variables temporales value1 y value2 hasta el final de la corrutina.
Para hacerle entender esto, hemos usado un tempvar3, que es 10 inicialmente y se actualiza a 13 y 16 por las llamadas posteriores de las corrutinas, ya que value1 se retiene como 3 durante la ejecución de la corrutina.
La primera coroutine.yield devuelve dos valores 4 y 3 a la función resume, que obtenemos actualizando los parámetros de entrada 3 y 2 en la declaración yield. También recibe el estado verdadero / falso de la ejecución de una rutina.
Otra cosa acerca de las corrutinas es cómo se gestionan los siguientes parámetros de la llamada de reanudación, en el ejemplo anterior; puede ver que la variable coroutine.yield recibe los siguientes parámetros de llamada, lo que proporciona una forma poderosa de realizar una nueva operación con la retención de los valores de parámetros existentes.
Finalmente, una vez que se ejecutan todas las declaraciones en las corrutinas, las llamadas posteriores volverán en falso y la declaración "no se puede reanudar la rutina muerta" como respuesta.
Otro ejemplo de corrutina
Veamos una corrutina simple que devuelve un número del 1 al 5 con la ayuda de la función de rendimiento y la función de reanudación. Crea una corrutina si no está disponible o reanuda la corrutina existente.
function getNumber()
local function getNumberHelper()
co = coroutine.create(function ()
coroutine.yield(1)
coroutine.yield(2)
coroutine.yield(3)
coroutine.yield(4)
coroutine.yield(5)
end)
return co
end
if(numberHelper) then
status, number = coroutine.resume(numberHelper);
if coroutine.status(numberHelper) == "dead" then
numberHelper = getNumberHelper()
status, number = coroutine.resume(numberHelper);
end
return number
else
numberHelper = getNumberHelper()
status, number = coroutine.resume(numberHelper);
return number
end
end
for index = 1, 10 do
print(index, getNumber())
end
Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado.
1 1
2 2
3 3
4 4
5 5
6 1
7 2
8 3
9 4
10 5
A menudo hay una comparación de las corrutinas con los subprocesos de los lenguajes de multiprogramación, pero debemos entender que las corrutinas tienen características similares de subprocesos, pero se ejecutan solo una a la vez y nunca se ejecutan al mismo tiempo.
Controlamos la secuencia de ejecución del programa para satisfacer las necesidades con la disposición de retener cierta información temporalmente. El uso de variables globales con corrutinas proporciona aún más flexibilidad a las corrutinas.