Diferença entre uma "corotina" e um "fio"?

Respostas:

122

As corotinas são uma forma de processamento seqüencial: apenas uma está sendo executada em um determinado momento (assim como os procedimentos das AKA das sub-rotinas - as funções do AKA - eles apenas passam o bastão entre si de maneira mais fluida).

Threads são (pelo menos conceitualmente) uma forma de processamento simultâneo: vários threads podem estar em execução a qualquer momento. (Tradicionalmente, em máquinas de CPU única e de núcleo único, essa simultaneidade era simulada com alguma ajuda do SO - atualmente, como muitas máquinas são com várias CPUs e / ou vários núcleos, os threads de fato estão sendo executados simultaneamente, não apenas "conceitualmente").

Alex Martelli
fonte
188

Primeira leitura: simultaneidade versus paralelismo - qual é a diferença?

Simultaneidade é a separação de tarefas para fornecer execução intercalada. Paralelismo é a execução simultânea de várias peças de trabalho para aumentar a velocidade. - https://github.com/servo/servo/wiki/Design

Resposta curta: com threads, o sistema operacional alterna a execução de threads preventivamente de acordo com seu planejador, que é um algoritmo no kernel do sistema operacional. Com as corotinas, o programador e a linguagem de programação determinam quando alternar as corotinas; em outras palavras, as tarefas são multitarefas de forma cooperativa ao pausar e retomar funções em pontos de ajuste, normalmente (mas não necessariamente) em um único encadeamento.

Resposta longa: Ao contrário dos encadeamentos, que são agendados preventivamente pelo sistema operacional, os comutadores de corotina são cooperativos, ou seja, o programador (e possivelmente a linguagem de programação e seu tempo de execução) controla quando um comutador ocorrerá.

Ao contrário dos encadeamentos, que são preventivos, os comutadores de corotina são cooperativos (o programador controla quando um comutador ocorrerá). O kernel não está envolvido nas opções de corotina. - http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html

Um idioma que suporta threads nativos pode executar seus threads (threads do usuário) nos threads do sistema operacional ( threads do kernel ). Todo processo possui pelo menos um thread do kernel. Os threads do kernel são como processos, exceto que compartilham espaço de memória em seu processo de propriedade com todos os outros threads desse processo. Um processo "possui" todos os recursos atribuídos, como memória, identificadores de arquivo, soquetes, identificadores de dispositivo etc., e esses recursos são todos compartilhados entre os threads do kernel.

O planejador do sistema operacional faz parte do kernel que executa cada encadeamento por um determinado período de tempo (em uma única máquina processadora). O planejador aloca tempo (divisão do tempo) para cada encadeamento e, se o encadeamento não for concluído nesse período, o agendador o pré-premia (interrompe e alterna para outro encadeamento). Vários threads podem ser executados em paralelo em uma máquina com vários processadores, pois cada thread pode ser (mas não necessariamente precisa ser) agendado em um processador separado.

Em uma máquina com um único processador, os threads são divididos em horários e antecipados (alternados) rapidamente (no Linux, o intervalo padrão é de 100 ms), o que os torna simultâneos. No entanto, eles não podem ser executados em paralelo (simultaneamente), pois um processador de núcleo único pode executar apenas uma coisa de cada vez.

Corotinas e / ou geradores podem ser usados ​​para implementar funções cooperativas. Em vez de serem executados nos encadeamentos do kernel e agendados pelo sistema operacional, eles são executados em um único encadeamento até render ou terminar, cedendo a outras funções, conforme determinado pelo programador. Idiomas com geradores , como Python e ECMAScript 6, podem ser usados ​​para criar corotinas. O assíncrono / espera (visto em C #, Python, ECMAscript 7, Rust) é uma abstração construída sobre as funções do gerador que produzem futuros / promessas.

Em alguns contextos, as corotinas podem se referir a funções de pilha, enquanto os geradores podem se referir a funções sem pilha .

Fibras , fios leves e fios verdes são outros nomes para corotinas ou coisas semelhantes a corotinas. Às vezes, podem parecer (normalmente de propósito) mais como threads do sistema operacional na linguagem de programação, mas não são executados em paralelo como threads reais e funcionam como corotinas. (Pode haver particularidades técnicas ou diferenças mais específicas entre esses conceitos, dependendo do idioma ou implementação.)

Por exemplo, Java tinha " linhas verdes "; esses eram encadeamentos agendados pela Java virtual machine (JVM) em vez de nativamente nos encadeamentos de kernel do sistema operacional subjacente. Eles não foram executados em paralelo ou se beneficiaram de vários processadores / núcleos - pois isso exigiria um encadeamento nativo! Como eles não foram agendados pelo sistema operacional, eles eram mais como coroutines do que threads do kernel. Threads verdes são o que o Java usou até os threads nativos serem introduzidos no Java 1.2.

Threads consomem recursos. Na JVM, cada encadeamento possui sua própria pilha, normalmente com 1 MB de tamanho. 64k é a menor quantidade de espaço de pilha permitido por encadeamento na JVM. O tamanho da pilha de encadeamentos pode ser configurado na linha de comandos para a JVM. Apesar do nome, os encadeamentos não são livres, devido aos recursos de uso, como cada encadeamento, que precisa de sua própria pilha, armazenamento local do encadeamento (se houver) e o custo de agendamento de encadeamentos / alternância de contexto / invalidação de cache da CPU. Isso é parte do motivo pelo qual as corotinas se tornaram populares para aplicativos críticos para o desempenho e altamente concorrentes.

O Mac OS permitirá apenas que um processo aloque cerca de 2000 threads, e o Linux aloca 8MB de pilha por thread e permitirá apenas quantos threads caberem na RAM física.

Portanto, os encadeamentos têm o peso mais pesado (em termos de uso de memória e tempo de alternância de contexto), depois as corotinas e, finalmente, os geradores são o peso mais leve.

llambda
fonte
2
+1, mas essa resposta pode se beneficiar de algumas referências.
Kojiro #
1
Linhas verdes são algo diferente de corotinas. eles não são? Até as fibras têm algumas diferenças. consulte programmers.stackexchange.com/questions/254140/…
113

Aproximadamente 7 anos atrasado, mas as respostas aqui estão faltando algum contexto em co-rotinas versus threads. Por que as corotinas estão recebendo tanta atenção ultimamente e quando eu as utilizaria em comparação com os threads ?

Antes de tudo, se as corotinas são executadas simultaneamente (nunca em paralelo ), por que alguém as preferiria aos threads?

A resposta é que as corotinas podem fornecer um nível muito alto de simultaneidade com muito pouca sobrecarga . Geralmente, em um ambiente de encadeamento, você tem no máximo 30 a 50 encadeamentos antes que a quantidade de sobrecarga desperdiçada realmente agende esses encadeamentos (pelo agendador do sistema) reduz significativamente a quantidade de tempo em que os encadeamentos realmente fazem um trabalho útil.

Ok, então com threads você pode ter paralelismo, mas não muito paralelismo, isso ainda não é melhor do que uma co-rotina em execução em um único thread? Bem, não necessariamente. Lembre-se de que uma co-rotina ainda pode fazer simultaneidade sem sobrecarga do agendador - ela simplesmente gerencia a própria alternância de contexto.

Por exemplo, se você tem uma rotina executando algum trabalho e ela executa uma operação que você sabe que bloqueará por algum tempo (por exemplo, uma solicitação de rede), com uma co-rotina, você pode mudar imediatamente para outra rotina sem a sobrecarga de incluir o agendador do sistema em esta decisão - sim, o programador deve especificar quando as co-rotinas podem mudar.

Com muitas rotinas fazendo trabalhos muito pequenos e alternando voluntariamente entre si, você alcançou um nível de eficiência que nenhum planejador jamais poderia alcançar. Agora você pode ter milhares de corotinas trabalhando juntas, em vez de dezenas de threads.

Como suas rotinas agora alternam entre si pontos pré-determinados, você também pode evitar o bloqueio de estruturas de dados compartilhadas (porque você nunca diria ao seu código para mudar para outra corotina no meio de uma seção crítica)

Outro benefício é o uso de memória muito menor. Com o modelo de encadeamento, cada encadeamento precisa alocar sua própria pilha e, portanto, o uso da memória aumenta linearmente com o número de encadeamentos que você possui. Com as co-rotinas, o número de rotinas que você possui não tem um relacionamento direto com o uso da memória.

E, finalmente, as co-rotinas estão recebendo muita atenção porque, em algumas linguagens de programação (como Python), seus threads não podem ser executados em paralelo de qualquer maneira - eles são executados simultaneamente como corotinas, mas sem pouca memória e sobrecarga de programação livre.

Martin Konecny
fonte
2
Como mudar para outra tarefa nas corotinas quando encontramos uma operação de bloqueio?
Narcisse Doudieu Siewe
A maneira como você alterna para outra tarefa é realizar qualquer operação de bloqueio assíncrona. Isso significa que você deve evitar o uso de qualquer operação que realmente bloqueie e use apenas operações que suportem o não bloqueio quando usadas no sistema de corotina. A única maneira de contornar isso é ter corotinas suportadas pelo kernel, como o UMS no Windows, por exemplo, onde ele salta para o seu agendador sempre que o "thread" do UMS é bloqueado em uma chamada de sistema.
precisa saber é o seguinte
@MartinKonecny ​​O TS Threads do C ++ recente está aderindo à abordagem que você mencionou?
Nikos #
Então, eventualmente, uma linguagem de programação moderna precisaria que ambas as Corotinas / Fibras utilizassem eficientemente um único núcleo da CPU para, por exemplo, operações pesadas que não sejam de computação, como IO e Threads, para paralelizar operações intensivas da CPU em muitos núcleos para ganhar velocidade, certo?
Mahatma_Fatal_Error 27/10
19

Em uma palavra: preempção. As corotinas agem como malabaristas que continuam entregando uns aos outros pontos bem ensaiados. Threads (threads verdadeiros) podem ser interrompidos em praticamente qualquer ponto e depois retomados mais tarde. Obviamente, isso traz consigo todos os tipos de problemas de conflito de recursos, daí o infame GIL - Global Interpreter Lock do Python.

Muitas implementações de encadeamentos são mais parecidas com corotinas.

Peter Rowell
fonte
9

Depende do idioma que você está usando. Por exemplo, em Lua, eles são a mesma coisa (o tipo variável de uma rotina é chamado thread).

Geralmente, embora as corotinas implementem a produção voluntária onde (você) o programador decide para onde yield, ou seja, concede o controle a outra rotina.

Os threads são gerenciados automaticamente (interrompidos e iniciados) pelo sistema operacional e podem até ser executados ao mesmo tempo em CPUs multicore.

Thomas Bonini
fonte
0

12 anos atrasado para a discussão, mas uma corrotina tem a explicação no nome. A corotina pode ser decomposta em Co e Rotina.

Uma rotina neste contexto é apenas uma sequência de operações / ações e, ao executar / processar uma rotina, a sequência de operações é executada uma a uma na mesma ordem exata especificada.

Co significa cooperação. Solicita-se que uma co-rotina (ou melhor espere) suspenda de bom grado sua execução para dar a outras co-rotinas a chance de executar também. Portanto, uma co-rotina é sobre o compartilhamento de recursos da CPU (voluntariamente), para que outras pessoas possam usar o mesmo recurso que você está usando.

Um encadeamento, por outro lado, não precisa suspender sua execução. Ser suspenso é completamente transparente ao encadeamento e o encadeamento é forçado pelo hardware subjacente a se suspender. Também é feito de uma maneira que seja transparente principalmente para o encadeamento, pois não é notificado e seu estado não é alterado, mas salvo e restaurado mais tarde quando é permitido que o encadeamento continue.

Uma coisa que não é verdade é que as co-rotinas não podem ser executadas simultaneamente e as condições de corrida não podem ocorrer. Depende do sistema em que as co-rotinas estão sendo executadas e é fácil possível criar co-rotinas de imagem.

Não importa como as co-rotinas se suspendem. De volta ao Windows 3.1, o int 03 foi inserido em qualquer programa (ou precisava ser colocado lá) e no C # adicionamos rendimento.

Martin Kersten
fonte