Mostrando entradas con la etiqueta resume. Mostrar todas las entradas
Mostrando entradas con la etiqueta resume. Mostrar todas las entradas

viernes, 16 de septiembre de 2011

Corutinas en Lua [Parte 2]

En el anterior post escribí una corutina creada con la función "coroutine.wrap()". Ahora toca hacerlo con "coroutine.create()":

function Print(valor1, valor2)
  while true do
    valor1, valor2 = coroutine.yield(valor1, valor2);
  end
end

PrintCo = coroutine.create(Print);
print(PrintCo);-->thread 0xXXXXXX
local a, b, c = coroutine.resume(PrintCo,"Hola", "mundo!")
print(a, b, c);-->true, Hola, mundo!
a, b, c = coroutine.resume(PrintCo,"Cómo", "estas?!");
print(a, b, c);-->true, Cómo, estas?!

Así es como se vería el mismo código pero creado con la función "coroutine.create()", la cual retorna un tipo definido como "thread". Para resumir una corutina creada con éste método debemos usar la función "coroutine.resume()", la cual retornará un valor booleano en el primer argumento y en el resto de los argumentos los valores que la función de la corutina regrese.

La principal diferencia de crearla así (en lugar de hacerlo con coroutine.wrap) es que podemos obtener el estado de ejecución de la misma (suspended, normal, dead) y además podemos manejar los errores sin propagarlos al invocador (con wrap el error se propaga directo al invocador).

Es importante volver a mencionar que todo lo que se escriba dentro del paréntesis de "coroutine.yield()" es lo que se retornará (ó en su defecto un simple return que finalizaría la corutina) al invocador de "coroutine.resume()" o a la función creada con wrap. El contenido del paréntesis no indican los nuevos valores a recibir/evaluar, así que podemos tenerlo vacío y aún así seguir recibiendo valores dentro de la corutina:


function Print(valor1, valor2)
  while true do
    print(valor1, valor2);
    valor1, valor2 = coroutine.yield();
  end
end

PrintCo = coroutine.create(Print);
print(PrintCo);-->thread 0xXXXXXX
local a = coroutine.resume(PrintCo,"Hola", "mundo!")
---->Hola, mundo!
print(a);-->true
a= coroutine.resume(PrintCo,"Cómo", "estas?!");
---->Cómo, estas?!
print(a);-->true

Todo esto es algo que, como ya mencioné en la publicación anterior, no esta muy bien documentado en los manuales de lua.

Ahora bien, la principal cualidad de una corutina es la de poder controlar varias corutinas desde 1 principal (resumiendo otras, pausando, etc.) logrando lo que se llama "multitasking" (multitareas) y no una función tan simple como la expuesta en los ejemplos. Pero, la finalidad de estos ejemplos es explicar de forma más detallada el uso básico de las corutinas.

NOTA: Todas éstas funciones pueden ser probadas para verificarlas en el demo online que ofrece el sitio de lua.org (http://www.lua.org/cgi-bin/demo).

jueves, 15 de septiembre de 2011

Corutinas en Lua

Hoy escribiré acerca de las corutinas de Lua (coroutines en inglés). Sé que la documentación de lua, manuales, etc. no logran explicar bien qué son ni cómo funcionan (y los ejemplos vaya que son difíciles de entender) así que trataré de facilitarlo para todos.

Las corutinas son nuevos procesos (threads) que se ejecutan independientemente de otros procesos. No se pueden ejecutar en paralelo, pero si en forma secuencial.

Muchos dirán: "De que sirven las corutinas si podemos simplemente ejecutar X función". Bueno, la corutinas son más rápidas en ejecución que una función porque ademas de estar cargadas en la memoria tambien se ejecutan en procesos independientes. Es decir, al ejecutar una función simplemente corremos una serie de instrucciones cargadas en X dirección de la memoria (pero sigue siendo el mismo proceso), y al ejecutar una corutina se ejecuta un proceso alterno que ya tiene cargadas la serie de instrucciones a seguir siendo más rápida la respuesta.

Pero no solo esto, sino también dichas corutinas (en procesos distintos) pueden pausarse, resumirse, etc.

En resumen, la ventajas son:

- La ejecución más rapida de funciones
- Poder controlar el flujo de otras corutinas desde dentro, o fuera, de otras.

Pero bueno, para dejarlo más claro escribiré un ejemplo muy básico de cómo crear una corutina:

function Print(valor1, valor2)
  while true do
    valor1, valor2 = coroutine.yield(valor1, valor2);
  end
end

PrintEx = coroutine.wrap(Print, a, b);
print(PrintEx("Hola", "mundo!"));
print(PrintEx("Cómo", "estas?"));

Con éste código creamos una corutina infinita (nunca se terminará) llamada "PrintEx" basada en la función "Print" que cada vez que la llamemos imprimirá los argumentos que le enviemos (bien podríamos simplemente usar la función print("Hola", "Mundo"); pero el caso es explicar las corutinas).

Bueno, ahora paso por paso.

1.- La función que será nuestra corutina es la función "Print" y recibe 2 argumentos.

2.- Dicha función inicia un loop infinito dónde por cada vuelta pausará la corutina usando "coroutine.yield()";

3.- La función "coroutine.yield(valor1, valor2);" lo que hace es retornar los argumentos suministrados por la función. Algo así como el clásico "return valor1, valor2;" en la funciones normales.

4.- Noten la línea de código "valor1, valor2 = coroutine.yield(valor1, valor2)". Como ya sabemos, la función "coroutine.yield();" retornará al invocante los argumentos que pongamos dentro del paréntesis. Pero si anteponemos "variable1, variable2 = " a dicha función entonces le estamos indicando a la corutina que la próxima vez que sea invocada los argumentos suministrados por el nuevo invocador serán guardados en las variables que antepongamos a "coroutine.yield()". De otro modo cada que la corutina seá invocada sólo retornaría los valores suministrados la primera vez que se le invocó.

5.- Usé como generador de corutina la función "coroutine.wrap()" (porque es más adecuado para este ejemplo), la cual retornará una nueva función que cada vez que sea invocada resumirá o ejecutará la corutina. En otras palabras, es lo mismo que usar "coroutine.resume(corutina, argumentos)" sólo que no retorna el primer valor boleano, sino sólo los argumentos que retorna la corutina.

Espero no sea tan complicada esta explicación, y más adelante escribiré otro ejemplo pero ahora si usando la función generadora "coroutine.create()".