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.