Os threads aparecem em duas perspectivas: sistemas operacionais e linguagens de programação. Nos dois casos, há alguma variação nos atributos de um encadeamento.
Uma definição mínima de um thread é que são coisas que acontecem em sequência, uma coisa após a outra.
Em um modelo de execução de máquina típico, cada encadeamento possui seu próprio conjunto de registradores de uso geral e seu próprio contador de programa. Se a máquina definir um registro específico como ponteiro de pilha, haverá uma cópia por encadeamento.
Da perspectiva do sistema operacional, o mínimo que um sistema operacional precisa fazer para oferecer suporte a threads é fornecer uma maneira de alternar entre eles. Isso pode acontecer automaticamente ( multitarefa premptiva ou apenas quando o encadeamento faz uma solicitação explícita (multitarefa cooperativa; nesse caso, os encadeamentos às vezes são chamados de fibras ). Existem também modelos híbridos com rendimentos de preferência e de cooperação, por exemplo, preempção entre encadeamentos de diferentes grupos ou tarefas, mas rendimentos explícitos entre threads do mesmo grupo / tarefa.A alternância entre threads envolve no mínimo salvar os valores de registro do thread antigo e restaurar os valores de registro do novo thread.
Em um sistema operacional multitarefa que fornece isolamento entre tarefas (ou processos , você pode tratar esses termos como sinônimos em um contexto do SO), cada tarefa possui seus próprios recursos, em particular o espaço de endereço, mas também abre arquivos, privilégios, etc. a ser fornecido pelo kernel do sistema operacional , uma entidade que está acima dos processos. Cada tarefa normalmente possui pelo menos um thread - uma tarefa que não executa código não é muito útil. O sistema operacional pode ou não suportar vários encadeamentos na mesma tarefa; por exemplo, o Unix original não. Uma tarefa ainda pode executar vários threads organizando a alternância entre eles - isso não requer privilégios especiais. Isso é chamado de " threads de usuário "”, Especialmente em um contexto Unix. Atualmente, a maioria dos sistemas Unix fornece threads do kernel, principalmente porque é a única maneira de ter vários threads do mesmo processo em execução em diferentes processadores.
A maioria dos recursos do sistema operacional, além do tempo de computação, é anexada a tarefas, não a threads. Alguns sistemas operacionais (por exemplo, Linux) delimitam explicitamente as pilhas; nesse caso, cada encadeamento possui o seu; mas existem sistemas operacionais nos quais o kernel não sabe nada sobre pilhas, eles são apenas parte do heap no que diz respeito. O kernel também geralmente gerencia um contexto de kernel para cada thread, que é uma estrutura de dados que contém informações sobre o que o thread está fazendo atualmente; isso permite que o kernel lide com vários threads bloqueados em uma chamada do sistema ao mesmo tempo.
No que diz respeito ao sistema operacional, os threads de uma tarefa executam o mesmo código, mas estão em posições diferentes nesse código (valores diferentes do contador de programa). Pode ou não acontecer que certas partes do código de um programa sejam sempre executadas em um thread específico, mas geralmente existe um código comum (por exemplo, funções utilitárias) que pode ser chamado de qualquer thread. Todos os threads veem os mesmos dados, caso contrário, seriam considerados tarefas diferentes; se alguns dados puderem ser acessados apenas por um encadeamento específico, isso geralmente é da competência da linguagem de programação, não do sistema operacional.
Na maioria das linguagens de programação, o armazenamento é compartilhado entre os threads do mesmo programa. Este é um modelo de memória compartilhada de programação simultânea; é muito popular, mas também propenso a erros, porque o programador precisa ter cuidado quando os mesmos dados puderem ser acessados por vários threads como podem ocorrer condições de corrida . Observe que mesmo variáveis locais podem ser compartilhadas entre threads: “variável local” (geralmente) significa uma variável cujo nome é válido apenas durante uma execução de uma função, mas outra thread pode obter um ponteiro para essa variável e acessá-la.
Também existem linguagens de programação em que cada thread tem seu próprio armazenamento, e a comunicação entre elas ocorre enviando mensagens pelos canais de comunicação. Isto é o modelo de transmissão de mensagens da programação simultânea. Erlangé a principal linguagem de programação que se concentra na passagem de mensagens; seu ambiente de execução possui um manuseio muito leve de threads e incentiva programas escritos com muitos threads de curta duração, em contraste com a maioria das outras linguagens de programação em que a criação de um thread é uma operação relativamente cara e o ambiente de tempo de execução não pode suportar um tamanho muito grande. número de threads ao mesmo tempo. O subconjunto seqüencial de Erlang (a parte da linguagem que ocorre em um thread, em particular a manipulação de dados) é (principalmente) puramente funcional; portanto, um thread pode enviar uma mensagem para outro thread que contém alguns dados e nenhum deles precisa se preocupar com os dados que estão sendo modificados pelo outro enquanto o estiver usando.
Alguns idiomas combinam os dois modelos, oferecendo armazenamento local de encadeamento, com ou sem um sistema de tipos para distinguir o local de armazenamento local de encadeamento dos globais. O armazenamento local de encadeamentos geralmente é um recurso de conveniência que permite que um nome de variável designe diferentes locais de armazenamento em encadeamentos diferentes.
Alguns acompanhamentos (difíceis) que podem ser interessantes para entender o que são tópicos:
- Qual é o mínimo que um kernel precisa fazer para suportar vários threads?
- Em um ambiente multiprocessador, o que é necessário para migrar um encadeamento de um processador para outro?
- O que seria necessário para implementar multithreading cooperativo ( corotinas ) em sua linguagem de programação favorita, sem suporte do sistema operacional e sem usar o suporte interno, se houver? (Lembre-se de que a maioria das linguagens de programação não possui as primitivas necessárias para implementar corotinas em um único encadeamento.)
- Como seria uma linguagem de programação se tivesse simultaneidade, mas nenhum conceito (explícito) de threads? (Exemplo principal: o cálculo pi ).