UMA co-rotina É semelhante a um thread, é uma linha de execução com sua própria pilha, suas próprias variáveis locais e seu próprio ponteiro para as instruções, mas com a particularidade de que compartilha variáveis globais e qualquer outro elemento com as outras co-rotinas.
Mas devemos esclarecer que existem diferenças entre os tópicos e as co-rotinas, a principal diferença é que um programa que usa threads as executa simultaneamente, o co-rotinas por outro lado, são colaborativos, onde um programa que utiliza co-rotinas executa apenas uma delas, e a suspensão destas só é alcançada se for explicitamente solicitada.
As co-rotinas Eles são extremamente poderosos, vamos ver o que este conceito abrange e como podemos usá-los em nossos programas.
Conceitos básicos
Todas as funções relacionadas a co-rotinas em Lua são encontrados na tabela de co-rotina, onde a função crio () nos permite criá-los, tem um argumento simples e é a função com o código que a co-rotina irá executar, onde seu retorno é um valor do tipo thread, que representa a nova co-rotina. Mesmo o argumento para criar a co-rotina às vezes é uma função anônima como no exemplo a seguir:
co = coroutine.create (function () print ("Hello Solvetic") end)UMA co-rotina pode ter quatro estados diferentes:
- suspenso
- com pressa
- morto
- normal
Quando o criamos, ele começa no estado interrompido, o que significa que a co-rotina não é executada automaticamente quando é criada pela primeira vez. O status de uma co-rotina pode ser consultado da seguinte forma:
imprimir (coroutine.status (co))Onde para sermos capazes de executar nossa co-rotina, só temos que usar a função de resume (), que o que ele faz internamente é alterar seu status de suspenso para em execução.
coroutine.resume (co)Se colocarmos todo o nosso código junto e adicionarmos uma linha adicional para consultar o status adicional de nossa co-rotina após fazer resume podemos ver todos os estados pelos quais ele passa:
co = coroutine.create (function () print ("Hello Solvetic") end) print (co) print (coroutine.status (co)) coroutine.resume (co) print (coroutine.status (co))Vamos ao nosso terminal e rodamos nosso exemplo, vamos ver a saída do nosso programa:
thread lua coroutines1.lua: 0x210d880 Suspended Hello Solvetic deadComo podemos ver a primeira impressão da co-rotina é o valor do thread, então temos o estado suspenso, e isso é bom, pois este é o primeiro estado durante a criação, depois com resume Executamos a co-rotina com a qual imprime a mensagem e depois disso seu status é mortoà medida que cumpria a sua missão.
As corrotinas à primeira vista podem parecer uma maneira complicada de chamar funções, mas são muito mais complexas do que isso. O poder do mesmo repousa em grande parte da função resultar () que permite suspender uma co-rotina em execução para retomar posteriormente o seu funcionamento, vejamos um exemplo da utilização desta função:
co = coroutine.create (function () para i = 1,10 do print ("resumindo a co-rotina", i) coroutine.yield () end end) coroutine.resume (co) coroutine.resume (co) coroutine.resume (co) coroutine .resume (co)O que esta função fará é executar até o primeiro resultar, e independentemente de termos um ciclo para, só vai imprimir de acordo com tantos resume Vamos ter para nossa co-rotina, para terminar vamos ver a saída através do terminal:
lua corrotinas 1. lua 1 2 3 4Essa seria a saída pelo terminal.
Filtros
Um dos exemplos mais claros que explicam as corrotinas é o caso de consumidor Y gerador de informação. Suponha então que temos uma função que gera continuamente alguns valores a partir da leitura de um arquivo e, em seguida, temos outra função que os lê, vamos ver um exemplo ilustrativo de como essas funções podem ser:
gerador de função () enquanto verdadeiro faz local x = io.read () send (x) end end função consumidor () enquanto verdadeiro faz local x = receber () io.write (x, "\ n") end endNeste exemplo, tanto o consumidor quanto o gerador funcionam sem nenhum tipo de descanso e podemos pará-los quando não houver mais informações para processar, porém o problema aqui é como sincronizar as funções de Mandar() Y receber(), já que cada um deles tem seu próprio loop e o outro é considerado um serviço que pode ser chamado.
Mas com as co-rotinas este problema pode ser resolvido de forma rápida e fácil, usando a função dupla currículo / rendimento podemos fazer nossas funções funcionarem sem problemas. Quando uma co-rotina chama a função resultar, ele não insere uma nova função, mas retorna uma chamada que está pendente e que só pode sair desse estado usando resume.
Da mesma forma quando chamando resume também não inicia uma nova função, ele retorna uma chamada de espera para resultar, resumindo este processo é o que precisamos para sincronizar as funções de Mandar() Y receber(). Aplicando esta operação, teríamos que usar receber() Aplicar resume ao gerador para gerar as novas informações e então Mandar() Aplique resultar Para o consumidor, vamos ver como nossas funções ficam com as novas mudanças:
função receive () local status, value = coroutine.resume (generator) return value end function send (x) coroutine.yield (x) end gen = coroutine.create (function () enquanto true do local x = io.read () enviar (x) fim fim)Mas ainda podemos melhorar nosso programa ainda mais, e é usando o filtros, que são tarefas que funcionam como geradores e consumidores ao mesmo tempo, fazendo um processo de transformação de informações muito interessante.
UMA filtro pode fazer resume de um gerador para obter novos valores e, em seguida, aplicar resultar para transformar dados para o consumidor. Vamos ver como podemos adicionar filtros facilmente ao nosso exemplo anterior:
gene = gerador () fil = filtro (gene) consumidor (fil)Como podemos ver, foi extremamente simples, onde além de otimizar nosso programa ganhamos pontos em legibilidade, importantes para futuras manutenções.
Corrotinas como iteradores
Um dos exemplos mais claros do gerador / consumidor é o iteradores presentes em ciclos recursivos, onde um iterador gera informações que serão consumidas pelo corpo dentro do ciclo recursivo, portanto, não seria absurdo usar co-rotinas para escrever esses iteradores, mesmo as co-rotinas possuem uma ferramenta especial para essa tarefa.
Para ilustrar o uso que podemos fazer de co-rotinas, vamos escrever um iterador para gerar as permutações de um determinado array, ou seja, colocar cada elemento de um array na última posição e invertê-lo, e então gerar recursivamente todas as permutações dos elementos restantes, vamos ver como nosso a função original seria sem incluir as corrotinas:
função print_result (var) para i = 1, #var do io.write (var [i], "") end io.write ("\ n") endAgora o que fazemos é mudar completamente este processo, primeiro mudamos o print_result () por rendimento, vamos ver a mudança:
função permgen (var1, var2) var2 = var2 ou # var1 if var2 <= 1 then coroutine.yield (var1) elseEste é um exemplo ilustrativo para demonstrar como os iteradores funcionam, no entanto Lua nos fornece uma função chamada enrolar que é semelhante a crioNo entanto, ele não retorna uma co-rotina, ele retorna uma função que, quando chamada, resume uma co-rotina. Então, para usar enrolar devemos apenas usar o seguinte:
permutações de função (var) return coroutine.wrap (function () permgen (var) end) endNormalmente, esta função é muito mais fácil de usar do que crio, pois nos dá exatamente o que precisamos, que é resumi-lo, porém é menos flexível, pois não nos permite verificar o estado da co-rotina criada com enrolar.
As corrotinas em Lua São uma ferramenta extremamente poderosa para lidar com tudo o que diz respeito a processos que devem ser executados em conjunto mas aguardando a conclusão de quem fornece a informação, também podemos ver a sua utilização para resolver problemas complexos no que diz respeito aos processos gerador / consumidor e também otimizando a construção de iteradores em nossos programas.