Voy a compartir con vosotros, una nueva característica de Lua que he estado estudiando, a pesar de ser un lenguaje interpretado permite multitarea. No es una multitarea "real", o preferente tal como la de POSIX que podemos encontrar en los lenguajes compilados, es una tarea "cooperativa". Esto quiere decir, que es el programador el que tiene que crear los puntos de interrupción en su código para dar paso a otras tareas, en vez de gestionar dichas interrupciones el Sistema Operativo o el lenguaje (como por ejemplo hace POSIX).
Mejor voy a explicar todo esto con un ejemplo, veréis que no es muy complicado. En primer lugar vamos a hacer un código normal, tiene 3 funciones con un código básico a las que hemos llamado proceso1, 2 y 3.
function proceso1() local n print("Proceso1: Imprime los números de 1 al 4 y luego del 4 al 1") for n = 1, 4 do print("proceso1: ", n) end --coroutine.yield() for n = 4, 1, -1 do print("proceso1: ", n) end end function proceso2() local j, n print("Proceso2: Imprime 2 veces los números de 1 al 3") for j = 1, 2 do for n = 1, 3 do print("proceso2 (ronda "..j.."): ",n) end --coroutine.yield() end end function proceso3(num) local n, total print("Proceso3: Recibe un número y le suma 5 en 3 veces, lo devuelve en cada suma") total = num for n = 1, 3 do total = total + 5 print("proceso3 (ronda "..n..") recibido "..num.." y acumulado:", total) end end proceso1() proceso2() proceso3(50)
La forma de ejecutar este código, lógicamente es llamando a una función o proceso cada vez. Si lo ejecutamos veríamos por la consola lo siguiente:
Proceso1: Imprime los números de 1 al 4 y luego del 4 al 1 proceso1: 1 proceso1: 2 proceso1: 3 proceso1: 4 proceso1: 4 proceso1: 3 proceso1: 2 proceso1: 1 Proceso2: Imprime 2 veces los números de 1 al 3 proceso2 (ronda 1): 1 proceso2 (ronda 1): 2 proceso2 (ronda 1): 3 proceso2 (ronda 2): 1 proceso2 (ronda 2): 2 proceso2 (ronda 2): 3 Proceso3: Recibe un número y le suma 5 en 3 veces, lo devuelve en cada suma proceso3 (ronda 1) recibido 50 y acumulado: 55 proceso3 (ronda 2) recibido 50 y acumulado: 60 proceso3 (ronda 3) recibido 50 y acumulado: 65
Fijaos bien en el código anterior, hasta que lo comprendáis, ES MUY IMPORTANTE para entender lo que viene después. Para facilitar las indicaciones, he numerado las lineas del siguiente listado.
1 function proceso1() 2 local n 3 print("Proceso1: Imprime los números de 1 al 4 y luego del 4 al 1") 4 for n = 1, 4 do 5 print("proceso1: ", n) 6 end 7 coroutine.yield() 8 for n = 4, 1, -1 do 9 print("proceso1: ", n) 10 end 11 end 12 13 function proceso2() 14 local j, n 15 print("Proceso2: Imprime 2 veces los números de 1 al 3") 16 for j = 1, 2 do 17 for n = 1, 3 do 18 print("proceso2 (ronda "..j.."): ",n) 19 end 20 coroutine.yield() 21 end 22 end 23 24 function proceso3(num) 25 local n, total 26 print("Proceso3: Recibe un número y le suma 5 en 3 veces, lo devuelve en cada suma") 27 total = num 28 for n = 1, 3 do 29 total = total + 5 30 print("proceso3 (ronda "..n..") recibido "..num.." y acumulado:", total) 31 coroutine.yield(total) 32 end 33 end 34 35 --Creamos las corutinas o "procesos" 36 coPro1 = coroutine.create(proceso1) 37 coPro2 = coroutine.create(proceso2) 38 coPro3 = coroutine.create(proceso3) 39 local valorPro3, foo 40 while true do 41 --Comprobamos que todas las corutinas hayan terminado 42 --status() devuelve "dead" cuando el proceso o función haya terminado 43 if coroutine.status(coPro1) == "dead" and 44 coroutine.status(coPro2) == "dead" and 45 coroutine.status(coPro3) == "dead" then 46 break 47 end 48 --Vamos ejecutando "las partes" de los procesos 49 coroutine.resume(coPro1) 50 coroutine.resume(coPro2) 51 foo, valorPro3 = coroutine.resume(coPro3, 50) 52 print("El valor del proceso3, ahora vale: ", valorPro3) 53 end
Lo primero que vemos es que en los procesos (en realidad son funciones, pero quiero llamarlo procesos para que comprendáis mejor el concepto de multitarea), en las lineas 7,20 y 31, hemos incluido una instrucción coroutine.yield() (ceder). Esto representa el punto donde queremos que nuestro proceso termine temporalmente y pase el control al proceso principal. En este ejemplo hay un yield() por proceso, pero se pueden poner los que quieran, eso sí, como veréis más adelante, es conveniente planificar bien dónde se colocan.
En las lineas 36, 37 y 38. Creamos las "corutinas", coPro1, 2, y 3. Contienen un puntero de un proceso. El parámetro es la función llamada. Por ejemplo en la linea 36, estamos diciendo que coPro1, es un proceso que apunta a la función proceso1.
En la linea 39, definimos 2 variables locales, que más adelante os diré para qué son. NOTA: Acostrumbraros a definir las variables como locales si no os pueden dar conflicto con otras en otros módulos con el mismo nombre, error MUY difícil de detectar.
En 40, creamos un bucle, este bucle funcionará hasta que todos los procesos terminen. La forma de hacer esto es comprobar el estado (status) de cada proceso como se hace en la linea 43. status devuelve "dead" si el proceso ha terminado (esta muerto vamos xDD).
Bien, ahora en 49, 50 y 51, viene quizás lo más importante, hacemos un resume (resumen) de los procesos. La primera vez que llamamos a resume(), se ejecuta el proceso hasta que encuentra una instrucción yield(), momento en el que regresa. Al volver a llamar a resume(), continúa donde lo dejó, y sigue hasta el yield(), así hasta que acaba el proceso.
En 51, he incluido estas variables para que veáis que se pueden enviar variables, y recibir resultados de los procesos. Fijaos que hemos puesto un segundo parámetro en resume, el valor 50, que es el parámetro que le enviamos al proceso3 inicialmente. Lua, puede devolver más de un valor, al contrario de lo que suele ser "normal" en otros lenguaje que devuelven uno o ninguno. yield() SIEMPRE devuelve true, y los parámetros que se le digan separados por comas, en este caso en la linea 31 yield(), sólo devuelve uno más, el total. Por cierto, no me preguntéis porque siempre devuelve true como primer parámetro y no le busquéis sentido porque no lo tiene, xDDDDD. El hecho de usar "foo", es poner una variable para que reciba el valor true que no nos interesa procesar.
Esta es la salida que produce el código usando corutinas:
Proceso1: Imprime los números de 1 al 4 y luego del 4 proceso1: 1 proceso1: 2 proceso1: 3 proceso1: 4 Proceso2: Imprime 2 veces los números de 1 al 3 proceso2 (ronda 1): 1 proceso2 (ronda 1): 2 proceso2 (ronda 1): 3 Proceso3: Recibe un número y le suma 5 en 3 veces, lo proceso3 (ronda 1) recibido 50 y acumulado: 55 El valor del proceso3, ahora vale: 55 proceso1: 4 proceso1: 3 proceso1: 2 proceso1: 1 proceso2 (ronda 2): 1 proceso2 (ronda 2): 2 proceso2 (ronda 2): 3 proceso3 (ronda 2) recibido 50 y acumulado: 60 El valor del proceso3, ahora vale: 60 proceso3 (ronda 3) recibido 50 y acumulado: 65 El valor del proceso3, ahora vale: 65
Se puede ver cómo se han ido alternando los diferente procesos, lo cual da una sensación de "multitarea".
Aunque esto sea una "multitarea cooperativa", en vez de una real o preferente, hace un apaño y más teniendo en cuenta de que se trata de un lenguaje interpretado. Espero que os haya servido de algo.
Los comentarios son de agradecer y motivan a crear más tutoriales ;-).
Un saludo.
Comentarios
Gracias.
Me sirvió de mucho este tema amigo (;.
Mil Gracias!
Interesante....ya queria saber como usar esas funciones de corrutinas.....en fin....aunque no sea multitarea real...es algo...Gracias!
Manual del Perfecto Votante Para un voto libre y justo!.
TheGCProjects
Icon0 sin linkear
Icon0 sin linkear
Listo.
Gracias por el aviso.
Re: Icon0 sin linkear
¿Cómor?
No te preocupes mucho, es un
No te preocupes mucho, es un error de edición del editor que te lo pasó a portada.
La verdad que muy útil el
La verdad que muy útil el tuto, y bien explicado con un buen ejemplo. Seguro que más de uno le vendrá bien saber esto para sus creaciones.
También se podría utilizar para el tratamiento de excepciones y errores de homebrew e intentar recuperarse, me equivoco?
orale muy buena opcion :D
orale muy buena opcion :D saludos sigue asi pspgorrister tus tutos son fantasticos n.n
:)
Tutoriales tan bien explicados como este me motivan a aprender lua, quizá despues de terminar de batallar con C XD
Saludos, y gracias ^_^