English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Coroutines de Lua

¿Qué es una cooperación (coroutine)?

Los programas cooperativos (coroutines) de Lua son muy similares a los hilos: tienen su propia pila, variables locales independientes, puntero de instrucciones independiente, y al mismo tiempo comparten variables globales y la mayoría de las otras cosas con otras cooperaciones.

La cooperación es una función muy poderosa, pero también muy compleja de usar.

Distinción entre hilos y programas cooperativos

La principal diferencia entre el hilo y la coroutine es que un programa con múltiples hilos puede ejecutar varios hilos al mismo tiempo, mientras que las coroutines necesitan ejecutarse en colaboración.

En cualquier momento especifico, solo una coroutine se está ejecutando, y la coroutine que se está ejecutando solo se suspenderá cuando se le pida explícitamente

Las coroutines son algo similar a múltiples hilos sincronizados, varios hilos esperando el mismo mutex tienen un poco de similitud con las coroutines.

Sintaxis básica

MétodoDescripción
coroutine.crear()Crea una coroutine, devuelve la coroutine, el parámetro es una función, cuando se usa con reanudar, despierta la llamada a la función
coroutine.reanudar()Reiniciar la coroutine, se usa con create
coroutine.rendir(coSuspender la coroutine, establecer la coroutine en estado suspendido, esto se puede usar con resume para obtener muchos efectos útiles
coroutine.estado()Ver el estado de la coroutine
Nota: el estado de la coroutine tiene tres: muerto, suspendido, corriendo, específicamente cuándo tiene tal estado, consulte el siguiente programa
coroutine.envolver()Crea una coroutine, devuelve una función, una vez que llamas a esta función, entras en la coroutine, tiene la misma función que create
coroutine.corriendo()Devuelve la coroutine corriendo, una coroutine es una hilo, cuando se usa corriendo, se devuelve el número de hilo de la coroutine

El siguiente ejemplo muestra el uso de los métodos anteriores:

-- archivo coroutine_test.lua
co = coroutine.crear(
    función(i)
        print(i);
    end
)
 
coroutine.reanudar(co, 1)   -- 1
print(coroutine.estado(co))  -- muerto
 
print("----------")
 
co = coroutine.envolver(
    función(i)
        print(i);
    end
)
 
co(1)
 
print("----------")
 
co2 = coroutine.crear(
    función()
        para i=1,10 hacer
            print(i)
            si i == 3 luego
                print(coroutine.estado(co2))  --corriendo
                print(coroutine.corriendo()) --hilo:XXXXXX
            end
            coroutine.rendir(co
        end
    end
)
 
coroutine.reanudar(co2) --1
coroutine.reanudar(co2) --2
coroutine.reanudar(co2) --3
 
print(coroutine.estado(co2))   -- suspendido
print(coroutine.corriendo())
 
print("----------")

El resultado de ejecutar el ejemplo anterior es:

1
muerto
----------
1
----------
1
2
3
corriendo
thread: 0x7fb801c05868    falso
suspendido
thread: 0x7fb801c04c88    true
----------

Se puede ver con coroutine.running, la implementación subyacente de coroutine es un hilo.

Cuando se crea una colaboración, se registra un evento en un nuevo hilo.

Cuando se utiliza resume para desencadenar eventos, se ejecuta la función coroutine de create, cuando se encuentra yield representa que se suspende la línea de hilo actual, esperando que se desencadene el evento de resume nuevamente.

A continuación, analizamos un ejemplo más detallado:

function foo (a)
    print("Salida de la función foo", a)
    return coroutine.yield(2 * a) -- devolver  2*El valor de a
end
 
co = coroutine.create(function (a , b)
    print("La salida de ejecución de la colaboración por primera vez", a, b) -- co-cuerpo 1 10
    local r = foo(a + 1)
     
    print("La salida de ejecución de la colaboración por segunda vez", r)
    local r, s = coroutine.yield(a + b, a - b)  -- Los valores de a, b son los ingresados por primera vez al llamar a la colaboración
     
    print("La salida de ejecución de la colaboración por tercera vez", r, s)
    return b, "Finalizar la colaboración"                   -- El valor de b es el传入第二次调用协作程序时的
end)
        
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--Línea de división----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---Línea de división---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---Línea de división---")
print("main", coroutine.resume(co, "x", "y")) -- no se puede recuperar la colaboración muerta
print("---Línea de división---")

El resultado de ejecutar el ejemplo anterior es:

La salida de ejecución de la colaboración por primera vez    1    10
Salida de la función foo    2
main  true    4
--Línea de división----
La salida de ejecución de la colaboración por segunda vez  r
main  true    11    -9
---Línea de división---
La salida de ejecución de la colaboración por tercera vez  x  y
main  true    10    Finalizar la colaboración
---Línea de división---
main  false  no se puede recuperar la colaboración muerta
---Línea de división---

El siguiente ejemplo es el siguiente:

  • Llamar a resume, despertar la colaboración, si la operación de resume tiene éxito devuelve true, de lo contrario devuelve false;

  • Ejecutar la colaboración;

  • Ejecutar hasta la sentencia yield;

  • Suspender de la colaboración, el primer resume devuelve;(Nota: aquí el yield devuelve, el parámetro es el de resume)

  • Segundo resume, despierta nuevamente la coroutine; (nota: los parámetros restantes en los parámetros de resume, además del primer parámetro, se usarán como parámetros de yield)

  • Se devuelve yield;

  • La coroutine continúa ejecutándose;

  • Si se llama al método resume en la coroutine después de que se complete su ejecución, se muestra: cannot resume dead coroutine

La fuerza de la combinación de resume y yield radica en que resume está en el contexto principal, introduce el estado externo (datos) al interior de la coroutine; mientras que yield devuelve el estado interno (datos) al contexto principal.

Productor-Problema del consumidor

Ahora voy a usar las coroutines de Lua para resolver el problema del productor-Este es el problema clásico del consumidor

local newProductor
function productor()
     local i = 0
     while true do
          i = i + 1
          send(i)     -- Enviar el artículo producido al consumidor
     end
end
function consumer()
     while true do
          local i = receive()     -- Obtener el artículo del productor
          print(i)
     end
end
function receive()
     local status, value = coroutine.resume(newProductor)
     return value
end
function send(x)
     coroutine.yield(x)     -- x representa el valor que necesita enviarse, después de que se devuelva el valor, la coroutine se suspenderá
end
-- Iniciar programa
newProductor = coroutine.create(productor)
consumer()

El resultado de ejecutar el ejemplo anterior es:

1
2
3
4
5
6
7
8
9
10
11
12
13
……