O que são corrotinas em c ++ 20?
De que forma é diferente de "Paralelismo2" ou / e "Simultaneidade2" (veja a imagem abaixo)?
A imagem abaixo é do ISOCPP.
O que são corrotinas em c ++ 20?
De que forma é diferente de "Paralelismo2" ou / e "Simultaneidade2" (veja a imagem abaixo)?
A imagem abaixo é do ISOCPP.
Respostas:
Em um nível abstrato, as co-rotinas dividem a ideia de ter um estado de execução da ideia de ter um thread de execução.
SIMD (instrução única, dados múltiplos) tem vários "threads de execução", mas apenas um estado de execução (funciona apenas em vários dados). Algoritmos indiscutivelmente paralelos são um pouco assim, em que você tem um "programa" rodando em dados diferentes.
Threading tem vários "threads de execução" e vários estados de execução. Você tem mais de um programa e mais de um thread de execução.
As corrotinas têm vários estados de execução, mas não possuem um thread de execução. Você tem um programa e o programa tem estado, mas não tem thread de execução.
O exemplo mais fácil de co-rotinas são geradores ou enumeráveis de outras linguagens.
Em pseudocódigo:
function Generator() {
for (i = 0 to 100)
produce i
}
O Generator
é chamado e, na primeira vez, ele retorna 0
. Seu estado é lembrado (o quanto o estado varia com a implementação de co-rotinas) e, da próxima vez que você chamá-lo, ele continuará de onde parou. Portanto, ele retorna 1 na próxima vez. Em seguida, 2.
Finalmente, ele atinge o final do loop e sai do final da função; a co-rotina está concluída. (O que acontece aqui varia de acordo com a linguagem da qual estamos falando; em python, isso gera uma exceção).
As corrotinas trazem esse recurso para C ++.
Existem dois tipos de corrotinas; empilhados e sem pilha.
Uma co-rotina sem pilha armazena apenas variáveis locais em seu estado e seu local de execução.
Uma co-rotina empilhável armazena uma pilha inteira (como um thread).
As co-rotinas sem pilha podem ser extremamente leves. A última proposta que li envolvia basicamente reescrever sua função em algo um pouco como lambda; todas as variáveis locais vão para o estado de um objeto, e rótulos são usados para pular de / para o local onde a co-rotina "produz" resultados intermediários.
O processo de produção de um valor é chamado de "rendimento", pois as co-rotinas são quase como multithreading cooperativo; você está devolvendo o ponto de execução ao chamador.
Boost tem uma implementação de corrotinas empilháveis; ele permite que você chame uma função para produzir para você. Corrotinas empilháveis são mais poderosas, mas também mais caras.
As co-rotinas são mais do que um simples gerador. Você pode esperar uma co-rotina em uma co-rotina, o que permite compor co-rotinas de uma maneira útil.
Corrotinas, como if, loops e chamadas de função, são outro tipo de "goto estruturado" que permite expressar certos padrões úteis (como máquinas de estado) de uma maneira mais natural.
A implementação específica de corrotinas em C ++ é um pouco interessante.
Em seu nível mais básico, ele adiciona algumas palavras-chave ao C ++:, co_return
co_await
co_yield
junto com alguns tipos de biblioteca que funcionam com elas.
Uma função se torna uma co-rotina por ter uma delas em seu corpo. Portanto, de acordo com sua declaração, eles são indistinguíveis de funções.
Quando uma dessas três palavras-chave é usada em um corpo de função, ocorre algum exame obrigatório padrão do tipo de retorno e dos argumentos e a função é transformada em uma co-rotina. Esse exame informa ao compilador onde armazenar o estado da função quando a função está suspensa.
A co-rotina mais simples é um gerador:
generator<int> get_integers( int start=0, int step=1 ) {
for (int current=start; true; current+= step)
co_yield current;
}
co_yield
suspende a execução das funções, armazena esse estado em generator<int>
e retorna o valor de current
por meio de generator<int>
.
Você pode fazer um loop sobre os inteiros retornados.
co_await
entretanto, permite emendar uma co-rotina em outra. Se você está em uma co-rotina e precisa dos resultados de uma coisa esperada (geralmente uma co-rotina) antes de progredir, você está co_await
nela. Se eles estiverem prontos, você prossegue imediatamente; caso contrário, você suspende até que o aguardado que você está esperando esteja pronto.
std::future<std::expected<std::string>> load_data( std::string resource )
{
auto handle = co_await open_resouce(resource);
while( auto line = co_await read_line(handle)) {
if (std::optional<std::string> r = parse_data_from_line( line ))
co_return *r;
}
co_return std::unexpected( resource_lacks_data(resource) );
}
load_data
é uma co-rotina que gera um std::future
quando o recurso nomeado é aberto e conseguimos analisar até o ponto onde encontramos os dados solicitados.
open_resource
e read_line
s são provavelmente corrotinas assíncronas que abrem um arquivo e leem linhas dele. O co_await
conecta o estado de suspensão e pronto de load_data
ao seu progresso.
As co-rotinas C ++ são muito mais flexíveis do que isso, pois foram implementadas como um conjunto mínimo de recursos de linguagem além dos tipos de espaço do usuário. Os tipos de espaço do usuário definem efetivamente o que co_return
co_await
e o co_yield
significado - tenho visto pessoas usá-los para implementar expressões opcionais monádicas, de modo que um co_await
opcional vazio propaga automaticamente o estado vazio para o opcional externo:
modified_optional<int> add( modified_optional<int> a, modified_optional<int> b ) {
return (co_await a) + (co_await b);
}
ao invés de
std::optional<int> add( std::optional<int> a, std::optional<int> b ) {
if (!a) return std::nullopt;
if (!b) return std::nullopt;
return *a + *b;
}
;;
.
Uma co-rotina é como uma função C que tem várias instruções de retorno e, quando chamada uma segunda vez, não inicia a execução no início da função, mas na primeira instrução após o retorno executado anteriormente. Este local de execução é salvo junto com todas as variáveis automáticas que viveriam na pilha em funções não co-rotinas.
Uma implementação de co-rotina experimental anterior da Microsoft usava pilhas copiadas para que você pudesse até mesmo retornar de funções aninhadas profundas. Mas esta versão foi rejeitada pelo comitê C ++. Você pode obter essa implementação, por exemplo, com a biblioteca de fibra Boosts.
supõe-se que as corrotinas sejam (em C ++) funções que são capazes de "esperar" a conclusão de alguma outra rotina e fornecer o que for necessário para que a rotina suspensa, em pausa, em espera continue. o recurso mais interessante para o pessoal do C ++ é que as corrotinas idealmente não ocupariam espaço na pilha ... C # já pode fazer algo assim com await e yield, mas o C ++ pode ter que ser reconstruído para obtê-lo.
a simultaneidade é fortemente focada na separação de interesses, onde um interesse é uma tarefa que o programa deve concluir. essa separação de interesses pode ser realizada por vários meios ... geralmente por delegação de algum tipo. a ideia de simultaneidade é que vários processos poderiam ser executados independentemente (separação de interesses) e um 'ouvinte' direcionaria tudo o que é produzido por esses interesses separados para onde deveria ir. isso depende muito de algum tipo de gerenciamento assíncrono. Existem várias abordagens para a concorrência, incluindo programação orientada a Aspect e outros. C # tem o operador 'delegate' que funciona muito bem.
paralelismo soa como simultaneidade e pode estar envolvido, mas na verdade é uma construção física envolvendo muitos processadores dispostos de forma mais ou menos paralela com software que é capaz de direcionar partes do código para diferentes processadores onde ele será executado e os resultados serão recebidos de volta sincronizadamente.