Multithreading: Qual é o objetivo de mais threads do que núcleos?

142

Eu pensei que o objetivo de um computador com vários núcleos é que ele pode executar vários threads simultaneamente. Nesse caso, se você possui uma máquina com quatro núcleos, qual o sentido de ter mais de 4 threads em execução por vez? Eles não estariam apenas roubando tempo (CPU Resources) um do outro?

Nick Heiner
fonte
52
nós gostamos deste tipo de perguntas, elas questionam o fundamental de algo, que é dado como certo ..
mantenha
6
Quando foi a última vez que o Firefox, o MS Word, o Winamp, o Eclipse e um gerenciador de downloads (mais de quatro programas / processos) foram executados simultaneamente na sua máquina quad core? Além disso, um único aplicativo às vezes pode gerar mais de quatro threads - e quanto a isso?
Amarghosh
1
Roubar não é necessariamente ruim. Você pode ter um encadeamento com prioridade mais alta para tarefas importantes que precisam roubar tempo.
Kichik
1
@Amarghosh Acho que essa era a pergunta: por que um aplicativo único pode gerar mais threads do que núcleos, se não parece trazer nenhum benefício de desempenho? E seu exemplo com mais de quatro programas não é muito relevante aqui. Como você observou corretamente, esses são processos. O recurso de multitarefa do SO (multiplexação de processos) tem muito pouco a ver com threads em um processo.
Aleksandr Ivannikov 20/0318

Respostas:

81

A resposta gira em torno do objetivo dos encadeamentos, que é o paralelismo: executar várias linhas de execução separadas ao mesmo tempo. Em um sistema 'ideal', você teria um thread em execução por núcleo: sem interrupção. Na realidade, este não é o caso. Mesmo se você tiver quatro núcleos e quatro threads de trabalho, o processo e os threads serão constantemente trocados por outros processos e threads. Se você estiver executando um sistema operacional moderno, todo processo possui pelo menos um thread e muitos têm mais. Todos esses processos estão em execução ao mesmo tempo. Você provavelmente tem várias centenas de threads em execução na sua máquina agora. Você nunca terá uma situação em que um thread é executado sem ter o tempo 'roubado' dele. (Bem, você pode se estiver executando em tempo real, se você estiver usando um sistema operacional em tempo real ou, mesmo no Windows, use uma prioridade de encadeamento em tempo real. Mas é raro.)

Com isso como pano de fundo, a resposta: Sim, mais de quatro threads em uma verdadeira máquina de quatro núcleos podem fornecer uma situação em que eles "roubam tempo um do outro", mas apenas se cada thread individual precisar de 100% da CPU . Se um encadeamento não estiver funcionando 100% (como um encadeamento da interface do usuário pode não estar, ou um encadeamento fazendo uma pequena quantidade de trabalho ou aguardando outra coisa), outro encadeamento agendado é realmente uma boa situação.

Na verdade, é mais complicado do que isso:

  • E se você tiver cinco partes do trabalho que precisam ser feitas de uma só vez? Faz mais sentido executá-los todos de uma vez, do que executar quatro deles e depois executar o quinto depois.

  • É raro um segmento realmente precisar de 100% da CPU. No momento em que usa E / S de disco ou de rede, por exemplo, pode ser possível que você gaste algum tempo esperando sem fazer nada útil. Esta é uma situação muito comum.

  • Se você tem um trabalho que precisa ser executado, um mecanismo comum é usar um pool de threads. Parece fazer sentido ter o mesmo número de threads que os núcleos, mas o pool de threads .Net tem até 250 threads disponíveis por processador . Não sei por que eles fazem isso, mas meu palpite é o tamanho das tarefas que são dadas para serem executadas nos threads.

Portanto, roubar tempo não é uma coisa ruim (e também não é um roubo: é como o sistema deve funcionar.) Escreva seus programas multithread com base no tipo de trabalho que os threads farão, o que pode não ser a CPU -limite. Descubra o número de threads necessários com base em perfis e medidas. Você pode achar mais útil pensar em termos de tarefas ou trabalhos, em vez de threads: escreva objetos de trabalho e entregue-os a um pool para execução. Finalmente, a menos que seu programa seja realmente crítico para o desempenho, não se preocupe muito :)

David
fonte
16
+1 para "mas apenas se cada segmento individual precisar de 100% da CPU". Essa foi a suposição que eu não percebi que estava fazendo.
Nick Heiner
Uma resposta maravilhosa para uma ótima pergunta. Obrigado!
Edgecase 04/07
53

Só porque um segmento existe nem sempre significa que ele está sendo executado ativamente. Muitas aplicações de encadeamentos envolvem alguns dos encadeamentos que vão dormir até que seja hora de fazer alguma coisa - por exemplo, entrada do usuário acionando encadeamentos para ativar, executar algum processamento e voltar a dormir.

Essencialmente, os encadeamentos são tarefas individuais que podem operar independentemente um do outro, sem a necessidade de estar ciente do progresso de outra tarefa. É bem possível ter mais desses recursos do que você pode executar simultaneamente; eles ainda são úteis por conveniência, mesmo que às vezes tenham que esperar na fila atrás um do outro.

Âmbar
fonte
11
Bem dito. O argumento 'um thread por CPU' se aplica somente ao código associado à CPU. A programação assíncrona é outro motivo para usar threads.
Joshua Davis
26

O ponto é que, apesar de não haver nenhuma aceleração real quando a contagem de threads excede a contagem de núcleos, é possível usar segmentos para desemaranhar partes da lógica que não deveriam ser interdependentes.

Mesmo em um aplicativo moderadamente complexo, o uso de um único encadeamento tenta fazer tudo rapidamente, cria um hash do 'fluxo' do seu código. O thread único passa a maior parte do tempo pesquisando isso, verificando isso, chamando condicionalmente as rotinas conforme necessário, e fica difícil ver algo além de um pântano de minúcias.

Compare isso com o caso em que você pode dedicar threads a tarefas para que, olhando para qualquer thread individual, possa ver o que esse thread está fazendo. Por exemplo, um encadeamento pode bloquear a espera na entrada de um soquete, analisar o fluxo em mensagens, filtrar mensagens e, quando uma mensagem válida aparecer, transmiti-la para outro encadeamento de trabalho. O segmento de trabalho pode trabalhar com entradas de várias outras fontes. O código para cada um deles exibirá um fluxo limpo e intencional, sem a necessidade de fazer verificações explícitas de que não há mais nada a fazer.

Particionar o trabalho dessa maneira permite que seu aplicativo conte com o sistema operacional para agendar o que fazer em seguida com a CPU, para que você não precise fazer verificações condicionais explícitas em todo o aplicativo sobre o que pode bloquear e o que está pronto para processar.

JustJeff
fonte
1
Esse é um pensamento interessante ... eu sempre ouvi dizer que um aplicativo multithreading é uma adição líquida de complexidade, mas o que você está dizendo faz sentido.
Nick Heiner
A multithreading de um aplicativo adiciona complexidade se suas preocupações não estiverem adequadamente separadas. Se ele foi projetado com sobreposição mínima de preocupações (e, portanto, estado compartilhado), é uma economia líquida em questões de complexidade.
APENAS MINHA OPINIÃO correta
Existem maneiras de estruturar aplicativos de thread único, para que o fluxo de controle seja mais claro no nível em que você escreve programas. OTOH, se você pode estruturar seus encadeamentos para que eles apenas passem mensagens um para o outro (em vez de ter recursos compartilhados), é muito simples descobrir o que está acontecendo e fazer tudo funcionar.
Donal Fellows
1
Deve-se ressaltar, no entanto, que o uso de threads só pode simplificar as coisas até um certo ponto. Com muita freqüência, é feita uma tentativa de fazer com que dois encadeamentos façam o trabalho que deve ser feito corretamente por um, no qual a complexidade volta em espadas. Os sintomas disso são excessivas necessidades de comunicação e sincronização para coordenar alguns resultados desejados.
JustJeff
15

Se um encadeamento estiver aguardando um recurso (como carregar um valor da RAM em um registro, E / S de disco, acesso à rede, iniciar um novo processo, consultar um banco de dados ou aguardar a entrada do usuário), o processador poderá trabalhar em um thread diferente e retorne ao primeiro thread assim que o recurso estiver disponível. Isso reduz o tempo que a CPU passa ociosa, pois ela pode executar milhões de operações em vez de ficar ociosa.

Considere um segmento que precisa ler dados de um disco rígido. Em 2014, um núcleo típico de processador opera a 2,5 GHz e pode executar 4 instruções por ciclo. Com um tempo de ciclo de 0,4 ns, o processador pode executar 10 instruções por nanossegundo. Com os tempos de busca típicos do disco rígido mecânico em torno de 10 milissegundos, o processador é capaz de executar 100 milhões de instruções no tempo necessário para ler um valor do disco rígido. Pode haver melhorias significativas no desempenho de discos rígidos com um pequeno cache (buffer de 4 MB) e unidades híbridas com alguns GB de armazenamento, pois a latência de dados para leituras sequenciais ou leituras da seção híbrida pode ser várias ordens de magnitude mais rapidamente.

Um núcleo de processador pode alternar entre threads (o custo para pausar e retomar uma thread é de cerca de 100 ciclos de clock) enquanto o primeiro thread aguarda uma entrada de alta latência (algo mais caro que registradores (1 clock) e RAM (5 nanossegundos)). E / S de disco, acesso à rede (latência de 250ms), leitura de dados de um CD ou barramento lento ou chamada de banco de dados. Ter mais threads do que núcleos significa que um trabalho útil pode ser feito enquanto as tarefas de alta latência são resolvidas.

A CPU possui um agendador de encadeamentos que atribui prioridade a cada encadeamento e permite que um encadeamento entre em suspensão e, em seguida, retoma após um tempo predeterminado. O trabalho do agendador de encadeamentos é reduzir o thrashing, o que ocorreria se cada encadeamento executasse apenas 100 instruções antes de voltar a dormir. A sobrecarga dos threads de comutação reduziria a taxa de transferência útil total do núcleo do processador.

Por esse motivo, convém dividir seu problema em um número razoável de threads. Se você estivesse escrevendo código para executar a multiplicação da matriz, a criação de um encadeamento por célula na matriz de saída pode ser excessiva, enquanto um encadeamento por linha ou por n linhas na matriz de saída pode reduzir o custo adicional de criação, pausa e retomada de encadeamentos.

É também por isso que a previsão de ramificação é importante. Se você tiver uma instrução if que exija o carregamento de um valor da RAM, mas o corpo das instruções if e else usar valores já carregados nos registradores, o processador poderá executar uma ou ambas as ramificações antes que a condição seja avaliada. Quando a condição retornar, o processador aplicará o resultado da ramificação correspondente e descartará a outra. A execução de um trabalho potencialmente inútil aqui é provavelmente melhor do que mudar para um thread diferente, o que pode levar a problemas.

À medida que nos mudamos dos processadores single-core de alta velocidade para os processadores multi-core, o design do chip concentrou-se em compactar mais núcleos por matriz, melhorando o compartilhamento de recursos no chip entre núcleos, melhores algoritmos de previsão de ramificação, melhor sobrecarga de troca de threads, e melhor agendamento de threads.

IceArdor
fonte
o mesmo pode ser feito com um único encadeamento e uma fila: \ existe realmente algum benefício em ter 80 encadeamentos em 2-4 núcleos, em vez de apenas 2-4 núcleos que acabam com as tarefas de uma fila assim que chegam e eles não têm nada para fazer?
Dmitry
8

A maioria das respostas acima fala sobre desempenho e operação simultânea. Vou abordar isso de um ângulo diferente.

Vamos considerar o caso de, digamos, um programa simplificado de emulação de terminal. Você precisa fazer o seguinte:

  • preste atenção nos caracteres recebidos do sistema remoto e exiba-os
  • preste atenção nas coisas vindas do teclado e envie-as para o sistema remoto

(Os emuladores de terminal reais fazem mais, inclusive potencialmente ecoando as coisas que você digita no visor, mas passaremos por cima disso por enquanto.)

Agora, o loop para leitura do controle remoto é simples, conforme o pseudocódigo a seguir:

while get-character-from-remote:
    print-to-screen character

O loop para monitorar o teclado e enviar também é simples:

while get-character-from-keyboard:
    send-to-remote character

O problema, porém, é que você precisa fazer isso simultaneamente. O código agora deve se parecer mais com isso se você não tiver o threading:

loop:
    check-for-remote-character
    if remote-character-is-ready:
        print-to-screen character
    check-for-keyboard-entry
    if keyboard-is-ready:
        send-to-remote character

A lógica, mesmo neste exemplo deliberadamente simplificado que não leva em conta a complexidade das comunicações no mundo real, é bastante ofuscada. No entanto, com o encadeamento, mesmo em um único núcleo, os dois loops de pseudocódigo podem existir independentemente, sem entrelaçar sua lógica. Como os dois encadeamentos serão basicamente vinculados à E / S, eles não sobrecarregam a CPU, mesmo sendo estritamente mais desperdiçados em recursos da CPU do que o loop integrado.

Agora, é claro, o uso no mundo real é mais complicado que o acima. Mas a complexidade do loop integrado aumenta exponencialmente à medida que você adiciona mais preocupações ao aplicativo. A lógica fica cada vez mais fragmentada e você precisa começar a usar técnicas como máquinas de estado, corotinas etc. para obter coisas gerenciáveis. Gerenciável, mas não legível. A segmentação mantém o código mais legível.

Então, por que você não usaria rosqueamento?

Bem, se suas tarefas são ligadas à CPU em vez de E / S, o encadeamento realmente torna o sistema lento. O desempenho sofrerá. Muito, em muitos casos. ("Thrashing" é um problema comum se você eliminar muitos threads vinculados à CPU. Você acaba gastando mais tempo alterando os threads ativos do que executando o conteúdo dos próprios threads.) Além disso, um dos motivos pelos quais a lógica acima é tão simples é que escolhi deliberadamente um exemplo simplista (e irrealista). Se você quiser repetir o que foi digitado na tela, terá um novo mundo de mágoas ao introduzir o bloqueio de recursos compartilhados. Com apenas um recurso compartilhado, isso não é um problema, mas começa a se tornar um problema cada vez maior, à medida que você tem mais recursos para compartilhar.

Então, no final, enfiar é sobre muitas coisas. Por exemplo, trata-se de tornar os processos vinculados à E / S mais responsivos (mesmo que menos eficientes no geral), como alguns já disseram. É também tornar a lógica mais fácil de seguir (mas apenas se você minimizar o estado compartilhado). Trata-se de muitas coisas, e você precisa decidir se suas vantagens superam suas desvantagens caso a caso.

APENAS MINHA OPINIÃO correta
fonte
6

Embora você certamente possa usar threads para acelerar os cálculos, dependendo do seu hardware, um dos principais usos deles é fazer mais de uma coisa por vez, por motivos de facilidade de uso.

Por exemplo, se você precisar fazer algum processamento em segundo plano e também permanecer responsivo à entrada da interface do usuário, poderá usar threads. Sem threads, a interface do usuário seria interrompida toda vez que você tentava executar qualquer processamento pesado.

Consulte também esta pergunta relacionada: Usos práticos para threads

Cam
fonte
A manipulação da interface do usuário é um exemplo clássico de uma tarefa vinculada a IO. Não é bom ter um único núcleo de CPU executando tarefas de processamento e de E / S.
Donal Fellows
6

Discordo totalmente da afirmação de @ kyoryu de que o número ideal é um thread por CPU.

Pense da seguinte maneira: por que temos sistemas operacionais de multiprocessamento? Na maior parte da história do computador, quase todos os computadores tinham uma CPU. No entanto, a partir da década de 1960, todos os computadores "reais" tinham sistemas operacionais de multiprocessamento (também conhecido como multitarefa).

Você executa vários programas para que um possa ser executado enquanto outros são bloqueados para coisas como E / S.

deixa de lado argumentos sobre se as versões do Windows anteriores ao NT eram multitarefas. Desde então, todo sistema operacional real tinha multitarefa. Alguns não o expõem aos usuários, mas estão lá de qualquer maneira, fazendo coisas como ouvir rádio no celular, conversar com o chip GPS, aceitar entrada do mouse etc.

Threads são apenas tarefas um pouco mais eficientes. Não há diferença fundamental entre uma tarefa, processo e thread.

Uma CPU é uma coisa terrível a ser desperdiçada; portanto, tenha muitas coisas prontas para usá-la quando puder.

Concordo que, com a maioria das linguagens procedurais, C, C ++, Java etc, escrever um código de thread adequado é muito trabalhoso. Com 6 CPUs principais no mercado hoje e 16 CPUs não muito distantes, espero que as pessoas se afastem desses idiomas antigos, pois o multi-threading é cada vez mais um requisito crítico.

Discordância com @kyoryu é apenas IMHO, o resto é fato.

fishtoprecords
fonte
5
Se você possui muitos encadeamentos vinculados ao processador , o número ideal é um por CPU (ou talvez um a menos, para deixar um para gerenciar toda a E / S, o SO e todas essas coisas). Se você possui threads vinculados à IO , pode empilhar bastante em uma única CPU. Aplicativos diferentes têm combinações diferentes de tarefas vinculadas ao processador e IO; isso é totalmente natural, mas é preciso ter cuidado com as declarações universais.
Donal Fellows
1
Obviamente, a diferença mais importante entre threads e processos é que no Windows não há fork (), portanto, a criação do processo é realmente cara, levando ao uso excessivo de threads.
Ninjalj 27/06
Exceto pelo dobramento de proteínas, SETI, etc., não há tarefas práticas do usuário que são vinculadas à computação por muito tempo. Sempre é necessário obter informações do usuário, conversar com o disco, conversar com o DBMS, etc. Sim, a despesa do fork () é uma das muitas coisas que Cutler amaldiçoou o NT com as outras pessoas no DEC.
fishtoprecords
5

Imagine um servidor da Web que precise atender a um número arbitrário de solicitações. Você precisa atender as solicitações em paralelo, pois, caso contrário, cada nova solicitação deverá aguardar até que todas as outras solicitações sejam concluídas (incluindo o envio da resposta pela Internet). Nesse caso, a maioria dos servidores da Web possui muito menos núcleos do que o número de solicitações que eles geralmente atendem.

Isso também facilita para o desenvolvedor do servidor: você só precisa escrever um programa de encadeamento que atenda a uma solicitação, não precisa armazenar várias solicitações, a ordem em que as atende e assim por diante.

tobiw
fonte
2
Você está escrevendo um software para um sistema operacional que suporta threading, mas não tem capacidade para multiplexação io? Eu acho que o servidor da web provavelmente é um mau exemplo, pois neste caso a multiplexação io quase sempre será mais eficiente do que gerar mais threads do que núcleos.
Jason Coco
3

Muitos threads permanecerão em espera, aguardando a entrada do usuário, E / S e outros eventos.

Cachorro
fonte
Com certeza. basta usar o Gerenciador de tarefas no Windows ou o TOP no sistema operacional real e ver quantas tarefas / processos estão em espera. É sempre 90% ou mais.
fishtoprecords
2

Os threads podem ajudar na capacidade de resposta em aplicativos de interface do usuário. Além disso, você pode usar threads para obter mais trabalho de seus núcleos. Por exemplo, em um único núcleo, você pode ter um thread fazendo IO e outro fazendo algum cálculo. Se tivesse um encadeamento único, o núcleo poderia estar essencialmente ocioso aguardando a conclusão do IO. Esse é um exemplo de nível bastante alto, mas os threads podem definitivamente ser usados ​​para sobrecarregar sua CPU um pouco mais.

Anon
fonte
Mais especificamente, um encadeamento pode estar aguardando E / S, enquanto outro faz o cálculo. Se a E / S fizesse ciclos (significativos) da CPU, não haveria benefício em executá-la em um encadeamento separado. O benefício é que seu encadeamento de computação pode ser executado enquanto o encadeamento de E / S está girando seus polegares, esperando que um grande cilindro de alumínio gire no lugar ou que pacotes cheguem pela Islândia, ou o que for.
Ken
2

Um processador, ou CPU, é o chip físico conectado ao sistema. Um processador pode ter vários núcleos (um núcleo é a parte do chip capaz de executar instruções). Um núcleo pode aparecer no sistema operacional como múltiplos processadores virtuais se for capaz de executar simultaneamente vários threads (um thread é uma única sequência de instruções).

Um processo é outro nome para um aplicativo. Geralmente, os processos são independentes um do outro. Se um processo morre, ele não causa a morte de outro processo. É possível que os processos se comuniquem ou compartilhem recursos como memória ou E / S.

Cada processo possui um espaço de endereço e uma pilha separados. Um processo pode conter vários threads, cada um capaz de executar instruções simultaneamente. Todos os threads em um processo compartilham o mesmo espaço de endereço, mas cada thread terá sua própria pilha.

Esperamos que com essas definições e pesquisas adicionais usando esses fundamentos ajude sua compreensão.

Srikar Doddi
fonte
2
Não vejo como isso resolve sua pergunta. Minha interpretação da pergunta dele é sobre o uso de núcleos de threads e o uso ideal dos recursos disponíveis, ou sobre o comportamento dos threads à medida que você aumenta seu número, ou algo assim nessas linhas.
David
@ David talvez não tenha sido uma resposta direta à minha pergunta, mas ainda sinto que aprendi lendo-a.
Nick Heiner
1

O uso ideal de threads é, de fato, um por núcleo.

No entanto, a menos que você use exclusivamente E / S assíncronas / sem bloqueio, há uma boa chance de que você tenha segmentos bloqueados no E / S em algum momento, o que não utilizará sua CPU.

Além disso, linguagens de programação típicas dificultam um pouco o uso de 1 thread por CPU. Os idiomas projetados para a simultaneidade (como o Erlang) podem facilitar o uso de threads extras.

kyoryu
fonte
O uso de threads para tarefas periódicas é um fluxo de trabalho muito comum e bem-vindo, e seria muito menos que o ideal se eles roubassem um núcleo.
Nick Bastin
@ Nick Bastin: Sim, mas é mais eficiente colocar essas tarefas em uma fila de tarefas e executá-las a partir dessa fila (ou de uma estratégia semelhante). Para uma eficiência ideal, 1 thread por núcleo supera tudo, pois evita sobrecarga de troca de contexto desnecessária e alocação de pilhas extras. Não importa o que aconteça, a tarefa periódica deve roubar um núcleo enquanto 'ativa', pois a CPU só pode realmente executar uma tarefa por núcleo (além de coisas como hyperthreading, se disponível).
Kyoryu
@ Nick Bastin: Infelizmente, como eu disse na resposta principal, a maioria das linguagens modernas não se presta bem à implementação fácil de um sistema que faz isso efetivamente não é trivial - você acaba lutando bastante no uso típico da linguagem.
Kyoryu
Meu argumento não é que um thread por núcleo não seja o ideal, é que um thread por núcleo é um sonho (a menos que você esteja incorporado) e projetar para tentar atingi-lo é uma perda de tempo, então você também pode faça o que for mais fácil para você (e não seja menos eficiente em um agendador moderno), em vez de tentar otimizar o número de threads que você está usando. Devemos girar tópicos sem uma boa razão? Claro que não, mas se você está desperdiçando desnecessariamente recursos do computador é uma preocupação, independentemente da segmentação.
Nick Bastin
@ Nick Bastin: Então, para resumir, um segmento por núcleo é o ideal, mas na verdade isso não é muito provável. Eu provavelmente deveria ter sido mais forte do que "um pouco difícil" ao falar sobre a probabilidade de realmente conseguir uma coisa dessas.
Kyoryu
1

Da maneira como algumas APIs são projetadas, você não tem escolha a não ser executá-las em um encadeamento separado (qualquer coisa com operações de bloqueio). Um exemplo seria as bibliotecas HTTP do Python (AFAIK).

Normalmente, isso não é um grande problema (se houver, o SO ou a API deverá ser fornecido com um modo operacional assíncrono alternativo, ou seja: select(2):), porque provavelmente significa que o encadeamento ficará inativo durante a espera de I / O conclusão. Por outro lado, se algo está fazendo um cálculo pesado, você tem que colocá-lo em um segmento separado do que dizer, o segmento de GUI (a menos que você gosta de multiplexação manual).

L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
fonte
1

Sei que essa é uma pergunta super antiga, com muitas boas respostas, mas estou aqui para apontar algo que é importante no ambiente atual:

Se você deseja criar um aplicativo para multiencadeamento, não deve projetar para uma configuração de hardware específica. A tecnologia da CPU vem avançando rapidamente há anos e as contagens principais estão aumentando constantemente. Se você projetar deliberadamente seu aplicativo de forma que ele use apenas 4 threads, estará potencialmente se restringindo a um sistema octa-core (por exemplo). Agora, até sistemas de 20 núcleos estão disponíveis comercialmente, portanto esse projeto definitivamente está fazendo mais mal do que bem.

Jai
fonte
0

Em resposta à sua primeira conjectura: máquinas com vários núcleos podem executar simultaneamente vários processos, não apenas os múltiplos threads de um único processo.

Em resposta à sua primeira pergunta: o objetivo de vários encadeamentos geralmente é executar simultaneamente várias tarefas em um aplicativo. Os exemplos clássicos na rede são um programa de e-mail enviando e recebendo e-mail e um servidor da web recebendo e enviando solicitações de páginas. (Observe que é essencialmente impossível reduzir um sistema como o Windows para executar apenas um thread ou mesmo apenas um processo. Execute o Gerenciador de tarefas do Windows e você verá uma longa lista de processos ativos, muitos dos quais executando vários threads. )

Em resposta à sua segunda pergunta: a maioria dos processos / encadeamentos não é vinculada à CPU (ou seja, não está sendo executada continuamente e sem interrupção), mas, em vez disso, pare e aguarde com frequência a conclusão da E / S. Durante essa espera, outros processos / threads podem ser executados sem "roubar" o código em espera (mesmo em uma máquina de núcleo único).

joe snyder
fonte
-5

Um encadeamento é uma abstração que permite que você escreva um código tão simples quanto uma sequência de operação, sem saber que o código é executado entrelaçado com outro código, ou estacionado esperando por E / S ou (talvez um pouco mais atento) à espera de outro encadeamento eventos ou mensagens.

KarlP
fonte
Eu poderia ter editado isso adicionando mais exemplos desde as votações negativas - mas um segmento (ou processo, neste contexto quase sem diferença) não foi inventado para aumentar o desempenho, mas para simplificar o código assíncrono e evitar escrever máquinas de estado complicadas que teve que lidar com todos os super estados possíveis no programa. De fato, havia tipicamente uma CPU, mesmo em servidores grandes. Estou curioso por que minha resposta é considerada anti-útil?
KarlP
-8

O ponto é que a grande maioria dos programadores não entende como projetar uma máquina de estado. Ser capaz de colocar tudo em seu próprio encadeamento libera o programador de ter que pensar em como representar com eficiência o estado de diferentes cálculos em andamento, para que possam ser interrompidos e depois retomados.

Como exemplo, considere a compactação de vídeo, uma tarefa que exige muita CPU. Se você estiver usando uma ferramenta GUI, provavelmente desejará que a interface permaneça responsiva (mostre progresso, responda a solicitações de cancelamento, redimensionamento de janelas etc.). Portanto, você projeta seu software de codificador para processar uma unidade grande (um ou mais quadros) de cada vez e executá-la em seu próprio encadeamento, separado da interface do usuário.

É claro que, uma vez que você perceba que seria bom poder salvar o estado de codificação em andamento para poder fechar o programa para reiniciar ou jogar um jogo que requer muitos recursos, você deve ter aprendido como projetar máquinas de estado a partir do começando. Ou você decide criar um novo problema de hibernação de processos no sistema operacional para suspender e retomar aplicativos individuais em disco ...

R .. GitHub PARE DE AJUDAR O GELO
fonte
7
Não vale (-1!), Mas é sério, essa é a coisa mais estúpida que eu já ouvi alguém dizer sobre esse assunto. Eu, por exemplo, não tenho problemas em implementar uma máquina de estado. Nenhum mesmo. Só não gosto de usá-los quando existem outras ferramentas que deixam para trás um código mais claro e fácil de manter . As máquinas estatais têm seus lugares e, nesses lugares, não podem ser comparadas. Entrelaçar operações intensivas da CPU com atualizações da GUI não é um desses locais. No mínimo, as corotinas são uma escolha melhor lá, com a segmentação sendo ainda melhor.
APENAS MINHA OPINIÃO correta
Para todos que modificam minha resposta, este NÃO é um argumento contra o uso de threads! Se você pode codificar uma máquina de estado que seja ótima, e com certeza faz sentido executar máquinas de estado em threads separados, mesmo que não seja necessário. Meu comentário foi que muitas vezes a escolha de usar threads é feita principalmente pelo desejo de evitar o design de máquinas de estado, que muitos programadores consideram "muito difíceis", em vez de oferecer qualquer outro benefício.
R .. GitHub Pare de ajudar o gelo