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.