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

Coroutines do Lua

What is a coroutine (coroutine)?

Lua coroutine (coroutine) is similar to threads: it has an independent stack, independent local variables, and an independent instruction pointer, while also sharing global variables and most other things with other coroutines.

Cooperation is a very powerful feature, but it is also very complex to use.

Thread and Cooperation Program Differences

A principal diferença entre thread e coroutine está no fato de que um programa com múltiplas threads pode executar várias threads ao mesmo tempo, enquanto as coroutines precisam funcionar em colaboração.

Apenas uma coroutine pode estar em execução em qualquer momento designado, e a coroutine em execução só pode ser suspensa explicitamente

As coroutines são um pouco semelhantes a threads sincronizadas, várias threads esperando o mesmo bloqueio de thread são um pouco semelhantes a coroutines.

Sintaxe básica

MétodoDescrição
coroutine.create()Cria uma coroutine, retorna a coroutine, o parâmetro é uma função, quando usado com resume, acorda a chamada da função
coroutine.resume()Reiniciar a coroutine, usado com create
coroutine.yield()Suspender a coroutine, configurar a coroutine para o estado suspenso, isso pode ser usado com resume para obter muitos efeitos úteis
coroutine.status()Verificar o estado da coroutine
Nota: o estado da coroutine tem três: morto, suspenso, em execução, quando tem esses estados, consulte o programa abaixo
coroutine.wrap()Cria uma coroutine, retorna uma função, uma vez que você chama essa função, entra na coroutine, tem a mesma função que create
coroutine.running()Retorna a coroutine em execução, uma coroutine é uma thread, quando usar running, retorna o número da thread da coroutine

O seguinte exemplo demonstra o uso de todos os métodos acima:

-- arquivo coroutine_test.lua
co = coroutine.create(
    função(i)
        print(i);
    end
)
 
coroutine.resume(co, 1)   -- 1
print(coroutine.status(co))  -- morto
 
print("----------")
 
co = coroutine.wrap(
    função(i)
        print(i);
    end
)
 
co(1)
 
print("----------")
 
co2 = coroutine.create(
    função()
        para i=1,10 faça
            print(i)
            se i == 3 então
                print(coroutine.status(co)2))  --em execução
                print(coroutine.running()) --thread:XXXXXX
            end
            coroutine.yield()
        end
    end
)
 
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
 
print(coroutine.status(co)2))   -- suspended
print(coroutine.running())
 
print("----------")

O resultado de execução dos exemplos acima é:

1
morto
----------
1
----------
1
2
3
em execução
thread: 0x7fb801c05868    falso
suspended
thread: 0x7fb801c04c88    true
----------

coroutine.running can be seen, coroutine is implemented as a thread in the underlying implementation.

When creating a coroutine, it is like registering an event in a new thread.

When using resume to trigger an event, the coroutine function of create is executed. When encountering yield, it means suspending the current thread and waiting for the next resume to trigger the event.

Next, we analyze a more detailed example:

function foo (a)
    print("foo function output", a)
    return coroutine.yield(2 * a) -- return  2*a's value
end
 
co = coroutine.create(function (a , b)
    print("the first coroutine execution output", a, b) -- co-body 1 10
    local r = foo(a + 1)
     
    print("the second coroutine execution output", r)
    local r, s = coroutine.yield(a + b, a - b)  -- a, b's value is the value passed when the coroutine is called for the first time
     
    print("the third coroutine execution output", r, s)
    return b, "end coroutine"                   -- b's value is the value passed when the coroutine is called for the second time
end)
        
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--division line----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---division line---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---division line---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---division line---")

O resultado de execução dos exemplos acima é:

the first coroutine execution output    1    10
foo function output    2
main  true    4
--division line----
the second coroutine execution output  r
main  true    11    -9
---division line---
the third coroutine execution output  x  y
main  true    10    end coroutine
---division line---
main  false  cannot resume dead coroutine
---division line---

the following example continues as follows:

  • call resume to wake up the coroutine, resume operation returns true if successful, otherwise returns false;

  • coroutine runs;

  • run to the yield statement;

  • yield suspend a coroutine, the first resume returns; (note: here yield returns, the parameter is resume's parameter)

  • Segunda chamada de resume, acordando novamente a coroutine; (Atenção: os parâmetros restantes do resume, além do primeiro, são os parâmetros do yield)

  • yield retorna;

  • A coroutine continua a executar;

  • Se o coroutine continuar a ser chamado após o término da execução, será exibido: cannot resume dead coroutine

A força da combinação de resume e yield está na capacidade de resume estar no contexto principal, inserindo o estado externo (dados) dentro da coroutine; enquanto yield retorna o estado interno (dados) para o contexto principal.

Produtor-Problema do Consumidor

Agora, eu vou usar coroutines do Lua para resolver o problema de produtor-Este é um problema clássico de consumidor.

local newProductor
function productor()
     local i = 0
     while true do
          i = i + 1
          send(i)     -- Enviar item produzido ao consumidor
     end
end
function consumer()
     while true do
          local i = receive()     -- Obter item do produtor
          print(i)
     end
end
function receive()
     local status, value = coroutine.resume(newProductor)
     return value
end
function send(x)
     coroutine.yield(x)     -- x representa o valor a ser enviado, após o valor ser retornado, a coroutine é pausada
end
-- Iniciar programa
newProductor = coroutine.create(productor)
consumer()

O resultado de execução dos exemplos acima é:

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