Lua - Corrotinas
Introdução
As corrotinas são de natureza colaborativa, o que permite que dois ou mais métodos sejam executados de maneira controlada. Com co-rotinas, a qualquer momento, apenas uma co-rotina é executada e esta corrotina em execução apenas suspende sua execução quando solicita explicitamente para ser suspensa.
A definição acima pode parecer vaga. Vamos supor que temos dois métodos, um o método do programa principal e uma co-rotina. Quando chamamos uma co-rotina usando a função resume, ela começa a executar e quando chamamos a função yield, ela suspende a execução. Novamente, a mesma co-rotina pode continuar a ser executada com outra chamada de função de retomada de onde foi suspensa. Este processo pode continuar até o final da execução da co-rotina.
Funções Disponíveis em Corrotinas
A tabela a seguir lista todas as funções disponíveis para co-rotinas em Lua e seu uso correspondente.
Sr. Não. | Método e Objetivo |
---|---|
1 | coroutine.create (f) Cria uma nova co-rotina com uma função f e retorna um objeto do tipo "thread". |
2 | coroutine.resume (co [, val1, ...]) Retoma a co-rotina e passa os parâmetros, se houver. Ele retorna o status da operação e outros valores de retorno opcionais. |
3 | coroutine.running () Retorna a co-rotina em execução ou nil se chamado no thread principal. |
4 | coroutine.status (co) Retorna um dos valores de execução, normal, suspenso ou morto com base no estado da co-rotina. |
5 | coroutine.wrap (f) Como coroutine.create, a função coroutine.wrap também cria uma co-rotina, mas em vez de retornar a própria corrotina, ela retorna uma função que, quando chamada, retoma a co-rotina. |
6 | coroutine.yield (...) Suspende a co-rotina em execução. O parâmetro passado para este método atua como valores de retorno adicionais para a função de retomada. |
Exemplo
Vejamos um exemplo para entender o conceito de co-rotinas.
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))
Quando executarmos o programa acima, obteremos a seguinte saída.
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
O que o exemplo acima faz?
Como mencionado antes, usamos a função de retomada para iniciar a operação e a função de rendimento para interromper a operação. Além disso, você pode ver que há vários valores de retorno recebidos pela função de retomada da co-rotina.
Primeiro, criamos uma co-rotina e atribuímos a ela um nome de variável co e a co-rotina leva duas variáveis como seus parâmetros.
Quando chamamos a primeira função de retomada, os valores 3 e 2 são retidos nas variáveis temporárias valor1 e valor2 até o final da co-rotina.
Para que você entenda isso, usamos um tempvar3, que é 10 inicialmente e é atualizado para 13 e 16 pelas chamadas subsequentes das co-rotinas, já que value1 é mantido como 3 durante a execução da co-rotina.
O primeiro coroutine.yield retorna dois valores 4 e 3 para a função de retomada, que obtemos atualizando os parâmetros de entrada 3 e 2 na declaração de rendimento. Ele também recebe o status verdadeiro / falso da execução da co-rotina.
Outra coisa sobre corrotinas é como os próximos parâmetros da chamada de currículo são atendidos, no exemplo acima; você pode ver que a variável coroutine.yield recebe os próximos parâmetros de chamada, o que fornece uma maneira poderosa de fazer uma nova operação com a retenção de valores de parâmetros existentes.
Finalmente, depois que todas as instruções nas corrotinas forem executadas, as chamadas subsequentes retornarão em falso e a instrução "não é possível retomar a co-rotina morta" como resposta.
Outro Exemplo de Corrotina
Vejamos uma co-rotina simples que retorna um número de 1 a 5 com a ajuda da função yield e da função resume. Ele cria co-rotina se não estiver disponível ou então retoma a co-rotina 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
Quando executarmos o programa acima, obteremos a seguinte saída.
1 1
2 2
3 3
4 4
5 5
6 1
7 2
8 3
9 4
10 5
Freqüentemente, há uma comparação de co-rotinas com threads de linguagens de multiprogramação, mas precisamos entender que as corrotinas têm recursos semelhantes de thread, mas executam apenas uma por vez e nunca são executadas simultaneamente.
Controlamos a seqüência de execução do programa para atender às necessidades com a prestação de reter certas informações temporariamente. O uso de variáveis globais com co-rotinas fornece ainda mais flexibilidade para as corrotinas.