Exemplos de corrotinas C++

Exemplos De Corrotinas C



As corrotinas fornecem um recurso de linguagem que permite escrever o código assíncrono de forma mais organizada e linear, promovendo uma abordagem estruturada e sequencial. Eles fornecem um mecanismo para pausar e reiniciar a execução de uma função em instâncias específicas sem interromper todo o thread. As corrotinas são úteis ao lidar com tarefas que exigem espera por operações de E/S, como leitura de um arquivo ou envio de uma chamada de rede.

As corrotinas são baseadas no conceito de geradores onde uma função pode produzir valores e posteriormente ser retomada para continuar a execução. As corrotinas fornecem uma ferramenta poderosa para gerenciar operações assíncronas e podem melhorar muito a qualidade geral do seu código.

Usos de corrotinas

As corrotinas são necessárias por vários motivos na programação moderna, especialmente em linguagens como C++. Aqui estão alguns motivos principais pelos quais as corrotinas são benéficas:







As corrotinas fornecem uma solução elegante para programação assíncrona. Eles permitem criar um código que parece sequencial e bloqueador, mais simples de raciocinar e compreender. As corrotinas podem suspender sua execução em pontos específicos sem bloquear as threads, possibilitando o funcionamento paralelo de outras tarefas. Por causa disso, os recursos do sistema podem ser utilizados de forma mais eficaz e a capacidade de resposta é aumentada em aplicações que envolvem operações de E/S ou espera por eventos externos.



Eles podem tornar o código mais fácil de entender e manter. Ao eliminar as complexas cadeias de retorno de chamada ou máquinas de estado, as corrotinas permitem que o código seja escrito em um estilo mais linear e sequencial. Isso melhora a organização do código, reduz o aninhamento e facilita a compreensão da lógica.



As corrotinas fornecem uma maneira estruturada de lidar com a simultaneidade e o paralelismo. Eles permitem expressar padrões de coordenação complexos e fluxos de trabalho assíncronos usando uma sintaxe mais intuitiva. Ao contrário dos modelos tradicionais de threading, onde os threads podem ser bloqueados, as corrotinas podem liberar recursos do sistema e permitir uma multitarefa eficiente.





Vamos criar alguns exemplos para demonstrar a implementação de corrotinas em C++.

Exemplo 1: Corrotinas Básicas

O exemplo básico de corrotinas é fornecido a seguir:



#include

#include

estrutura Este Corout {

estrutura tipo_promessa {

ThisCorout get_return_object ( ) { retornar { } ; }

padrão :: suspender_nunca suspensão_inicial ( ) { retornar { } ; }

padrão :: suspender_nunca suspensão_final ( ) não, exceto { retornar { } ; }

vazio exceção_não tratada ( ) { }

vazio retorno_void ( ) { }

} ;

bool aguarda_ready ( ) { retornar falso ; }

vazio aguardar_suspender ( padrão :: coroutine_handle <> h ) { }

vazio aguardar_resume ( ) { padrão :: corte << 'A Corrotina é retomada.' << padrão :: fim ; }

} ;

Este Corout foo ( ) {

padrão :: corte << 'A corrotina começou.' << padrão :: fim ;

co_await std :: suspender_sempre { } ;

co_retorno ;

}

interno principal ( ) {

auto cr = foo ( ) ;

padrão :: corte << 'A corrotina foi criada.' << padrão :: fim ;

cr. aguardar_resume ( ) ;

padrão :: corte << 'Corotina terminada.' << padrão :: fim ;

retornar 0 ;

}

Vamos examinar o código fornecido anteriormente e explicá-lo em detalhes:

Depois de incluir os arquivos de cabeçalho necessários, definimos a estrutura “ThisCorout” que representa uma corrotina. Dentro de “ThisCorout”, é definida outra estrutura que é “promise_type” que trata da promessa da corrotina. Essa estrutura fornece várias funções exigidas pelo maquinário de co-rotina.

Dentro dos colchetes, utilizamos a função get_return_object(). Ele retorna o próprio objeto da co-rotina. Neste caso, ele retorna um objeto “ThisCorout” vazio. Em seguida, a função inicial_suspend() é invocada, determinando o comportamento quando a corrotina é iniciada pela primeira vez. O std::suspend_never significa que a corrotina não deve ser suspensa inicialmente.

Depois disso, temos a função final_suspend() que determina o comportamento quando a corrotina está prestes a terminar. O std::suspend_never significa que a corrotina não deve ser suspensa antes de sua finalização.

Se uma corrotina lançar uma exceção, o método unhandled_exception() será invocado. Neste exemplo, é uma função vazia, mas você pode tratar as exceções conforme necessário. Quando a corrotina termina sem produzir um valor, o método return_void() é invocado. Neste caso, também é uma função vazia.

Também definimos três funções-membro em “ThisCorout”. A função wait_ready() é chamada para verificar se a corrotina está pronta para retomar a execução. Neste exemplo, sempre retorna falso, o que indica que a corrotina não está pronta para ser retomada imediatamente. Quando a corrotina for suspensa, o método wait_suspend() é chamado. Aqui, é uma função vazia, o que significa que nenhuma suspensão é necessária. O programa chama await_resume() quando a corrotina é retomada após a suspensão. Ele apenas gera uma mensagem informando que a corrotina foi retomada.

As próximas linhas do código definem a função de co-rotina foo(). Dentro de foo(), começamos imprimindo uma mensagem informando que a corrotina foi iniciada. Em seguida, co_await std::suspend_always{} é usado para suspender a corrotina e indica que ela pode ser retomada posteriormente. A instrução co_return é usada para finalizar a corrotina sem retornar nenhum valor.

Na função main(), construímos um objeto “cr” do tipo “ThisCorout” chamando foo(). Isso cria e inicia a corrotina. Em seguida, é impressa uma mensagem informando que a corrotina foi criada. A seguir, chamamos await_resume() no objeto de co-rotina “cr” para retomar sua execução. Dentro de wait_resume(), a mensagem “A corrotina foi retomada” é impressa. Por fim, exibimos uma mensagem informando que a corrotina foi concluída antes do programa terminar.

Ao executar este programa, a saída é a seguinte:

Exemplo 2: Corrotina com Parâmetros e Rendimento

Agora, para esta ilustração, fornecemos um código que demonstra o uso de corrotinas com parâmetros e rendimento em C++ para criar um comportamento semelhante a um gerador para produzir uma sequência de números.

#include

#include

#incluir

estrutura NEWCorotina {

estrutura tipo_p {

padrão :: vetor < interno > valores ;

NEWCoroutine get_return_object ( ) { retornar { } ; }

padrão :: suspender_sempre suspensão_inicial ( ) { retornar { } ; }

padrão :: suspender_sempre suspensão_final ( ) não, exceto { retornar { } ; }

vazio exceção_não tratada ( ) { }

vazio retorno_void ( ) { }

padrão :: suspender_sempre valor_rendimento ( interno valor ) {

valores. retrocesso ( valor ) ;

retornar { } ;

}

} ;

padrão :: vetor < interno > valores ;

estrutura iterador {

padrão :: coroutine_handle <> chorus_handle ;

operador booleano != ( const iterador & outro ) const { retornar chorus_handle != outro. chorus_handle ; }

iterador & operador ++ ( ) { chorus_handle. retomar ( ) ; retornar * esse ; }

interno operador * ( ) const { retornar chorus_handle. promessa ( ) . valores [ 0 ] ; }

} ;

início do iterador ( ) { retornar iterador { padrão :: coroutine_handle < tipo_p >:: de_promessa ( promessa ( ) ) } ; }

fim do iterador ( ) { retornar iterador { nullptr } ; }

padrão :: coroutine_handle < tipo_p > promessa ( ) { retornar
padrão :: coroutine_handle < tipo_p >:: de_promessa ( * esse ) ; }

} ;

NEWCoroutine generateNumbers ( ) {

co_rendimento 5 ;

co_rendimento 6 ;

co_rendimento 7 ;

}

interno principal ( ) {

NEWCoroutine nc = gerar números ( ) ;

para ( interno valor : não ) {

padrão :: corte << valor << ' ' ;

}

padrão :: corte << padrão :: fim ;

retornar 0 ;

}

No código anterior, a estrutura NEWCoroutine representa um gerador baseado em corrotina. Ele contém uma estrutura “p_type” aninhada que serve como tipo de promessa para a corrotina. A estrutura p_type define as funções exigidas pelo mecanismo de corrotina, como get_return_object(), inicial_suspend(), final_suspend(), unhandled_exception() e return_void(). A estrutura p_type também inclui a função yield_value(int value) que é usada para gerar os valores da corrotina. Adiciona o valor fornecido ao vetor de valores.

A estrutura NEWCoroutine inclui a variável de membro std::vector chamada “valores”, que representa os valores gerados. Dentro da NEWCoroutine, existe um iterador de estrutura aninhado que permite iterar sobre os valores gerados. Ele contém um coro_handle que é um identificador para a corrotina e define os operadores como! =, ++ e * para iteração.

Usamos a função Begin() para criar um iterador no início da corrotina, obtendo o coro_handle da promessa p_type. Enquanto a função end() cria um iterador que representa o fim da corrotina e é construído com um nullptr coro_handle. Depois disso, a função promessa() é utilizada para retornar o tipo de promessa criando um coroutine_handle a partir da promessa p_type. A função generateNumbers() é uma corrotina que produz três valores – 5, 6 e 7 – usando a palavra-chave co_yield.

Na função main(), uma instância de NEWCoroutine chamada “nc” é criada invocando a corrotina generateNumbers(). Isso inicializa a corrotina e captura seu estado. Um loop “for” baseado em intervalo é usado para iterar sobre os valores de “nc”, e cada valor é impresso, separado por um espaço usando std::cout.

A saída gerada é a seguinte:

Conclusão

Este artigo demonstra a utilização de corrotinas em C++. Discutimos dois exemplos. Para a primeira ilustração, a corrotina básica é criada em um programa C++ usando as funções de corrotina. Enquanto a segunda demonstração foi realizada utilizando as corrotinas com parâmetros e produzindo um comportamento semelhante ao de um gerador para criar uma sequência de números.