Recentemente, ouvi algumas pessoas dizerem que, no Linux, quase sempre é melhor usar processos em vez de threads, pois o Linux é muito eficiente no processamento de processos e porque há muitos problemas (como bloqueio) associados a threads. No entanto, desconfio, porque parece que os threads podem proporcionar um ganho de desempenho bastante grande em algumas situações.
Portanto, minha pergunta é: quando confrontados com uma situação que threads e processos possam lidar muito bem, devo usar processos ou threads? Por exemplo, se eu estivesse escrevendo um servidor Web, devo usar processos ou threads (ou uma combinação)?
linux
performance
multithreading
process
user17918
fonte
fonte
Respostas:
O Linux usa um modelo de segmentação 1-1, sem (para o kernel) nenhuma distinção entre processos e threads - tudo é simplesmente uma tarefa executável. *
No Linux, a chamada do sistema
clone
clona uma tarefa, com um nível configurável de compartilhamento, entre os quais:CLONE_FILES
: compartilhe a mesma tabela de descritor de arquivo (em vez de criar uma cópia)CLONE_PARENT
: não estabeleça uma relação pai-filho entre a nova tarefa e a antiga (caso contrário,getppid()
= paigetpid()
)CLONE_VM
: compartilhe o mesmo espaço de memória (em vez de criar um COW cópia )fork()
chamadasclone(
menos compartilhamento)
epthread_create()
chamadasclone(
mais compartilhamento)
. **fork
Isso custa um pouco mais do que o custopthread_create
por causa da cópia de tabelas e da criação de mapeamentos de COW para memória, mas os desenvolvedores do kernel do Linux tentaram (e conseguiram) minimizar esses custos.A alternância de tarefas, se compartilharem o mesmo espaço de memória e várias tabelas, será um pouco mais barata do que se não forem compartilhadas, porque os dados já podem estar carregados no cache. No entanto, a alternância de tarefas ainda é muito rápida, mesmo que nada seja compartilhado - isso é algo que os desenvolvedores de kernel do Linux tentam garantir (e conseguem garantir).
De fato, se você estiver em um sistema com vários processadores, o compartilhamento não será realmente benéfico para o desempenho: se cada tarefa estiver sendo executada em um processador diferente, a sincronização da memória compartilhada será cara.
* Simplificado.
CLONE_THREAD
faz com que a entrega de sinais seja compartilhada (que precisaCLONE_SIGHAND
, que compartilha a tabela do manipulador de sinais).** simplificado. Existem dois
SYS_fork
eSYS_clone
syscalls, mas no kernel, osys_fork
esys_clone
são invólucros muito finos com a mesmado_fork
função, que por si só é um invólucro finocopy_process
. Sim, os termosprocess
,thread
etask
são usados em vez de forma intercambiável no kernel do Linux ...fonte
socket
,bind
,listen
,fork
, e, em seguida, ter vários processosaccept
conexões no mesmo soquete de audição. Um processo pode parar de aceitar se estiver ocupado, e o kernel encaminhará as conexões de entrada para outro processo (se ninguém estiver ouvindo, o kernel irá enfileirar ou soltar, dependendo dalisten
lista de pendências). Você não tem muito mais controle sobre a distribuição de trabalho do que isso, mas geralmente isso é bom o suficiente!clone()
determinar quais recursos são compartilhados. Uma tarefa também podeunshare()
recursos a qualquer momento posterior.task_struct
para cada tarefa. Isso geralmente é chamado de "processo" em todo o código do kernel, mas corresponde a cada thread executável. Não existeprocess_struct
; se váriostask_struct
s estão vinculados por suathread_group
lista, eles são o mesmo "processo" para o espaço do usuário. Há um pouco de manipulação especial de "threads" s, por exemplo, todos os threads irmãos são interrompidos no fork e no exec, e apenas o thread "main" aparecels /proc
. Cada thread é acessível via/proc/pid
, seja ele listado/proc
ou não.clone(CLONE_THREAD | CLONE_VM | CLONE_SIGHAND))
forneceria um novo "encadeamento" que não compartilha o diretório de trabalho, arquivos ou bloqueios, enquantoclone(CLONE_FILES | CLONE_FS | CLONE_IO)
o "processo" que o faz. O sistema subjacente cria tarefas por clonagem;fork()
epthread_create()
são apenas funções de biblioteca que invocam de maneiraclone()
diferente (como escrevi nesta resposta).O Linux (e de fato o Unix) oferece uma terceira opção.
Opção 1 - processos
Crie um executável independente que lide com parte (ou todas as partes) do seu aplicativo e chame-o separadamente para cada processo, por exemplo, o programa executa cópias de si mesmo para delegar tarefas.
Opção 2 - threads
Crie um executável independente que seja iniciado com um único thread e crie threads adicionais para executar algumas tarefas
Opção 3 - garfo
Disponível apenas no Linux / Unix, isso é um pouco diferente. Um processo bifurcado é realmente seu próprio processo com seu próprio espaço de endereço - não há nada que a criança possa fazer (normalmente) para afetar o espaço de endereço de seus pais ou irmãos (diferente de um encadeamento) - para obter mais robustez.
No entanto, as páginas da memória não são copiadas, elas são copiadas na gravação; portanto, geralmente é usada menos memória do que você imagina.
Considere um programa de servidor da web que consiste em duas etapas:
Se você usasse threads, a etapa 1 seria executada uma vez e a etapa 2, executada em vários threads. Se você usou processos "tradicionais", as etapas 1 e 2 precisariam ser repetidas para cada processo, e a memória para armazenar os dados de configuração e tempo de execução duplicados. Se você usou o fork (), poderá executar a etapa 1 uma vez e depois o fork (), deixando os dados e a configuração do tempo de execução na memória, intocados, não copiados.
Portanto, existem realmente três opções.
fonte
Isso depende de muitos fatores. Os processos são mais pesados que os threads e têm um custo mais alto de inicialização e desligamento. A comunicação entre processos (IPC) também é mais difícil e mais lenta que a comunicação entre segmentos.
Por outro lado, os processos são mais seguros e mais seguros que os threads, porque cada processo é executado em seu próprio espaço de endereço virtual. Se um processo falha ou tem uma saturação de buffer, ele não afeta nenhum outro processo, enquanto que se um thread falha, ele remove todos os outros threads no processo e, se um segmento tiver uma saturação de buffer, ele será aberto. uma falha de segurança em todos os segmentos.
Portanto, se os módulos do seu aplicativo puderem ser executados de forma independente e com pouca comunicação, você provavelmente deverá usar processos se puder arcar com os custos de inicialização e desligamento. O impacto no desempenho do IPC será mínimo e você ficará um pouco mais seguro contra bugs e falhas de segurança. Se você precisar de todo o desempenho que puder obter ou ter muitos dados compartilhados (como estruturas de dados complexas), use threads.
fonte
Outros discutiram as considerações.
Talvez a diferença importante seja que, nos processos do Windows, sejam pesados e caros em comparação com os threads, e no Linux a diferença é muito menor; portanto, a equação se equilibra em um ponto diferente.
fonte
Era uma vez o Unix e, nesse bom e velho Unix, havia muita sobrecarga para os processos; portanto, o que algumas pessoas inteligentes faziam era criar threads, que compartilhavam o mesmo espaço de endereço com o processo pai e eles só precisavam de um contexto reduzido. switch, o que tornaria o contexto mais eficiente.
Em um Linux contemporâneo (2.6.x), não há muita diferença no desempenho entre uma alternância de contexto de um processo em comparação com um encadeamento (apenas o material da MMU é adicional para o encadeamento). Há um problema com o espaço de endereço compartilhado, o que significa que um ponteiro com defeito em um thread pode corromper a memória do processo pai ou outro thread no mesmo espaço de endereço.
Um processo é protegido pela MMU, portanto, um ponteiro com defeito causará apenas um sinal 11 e nenhuma corrupção.
Em geral, eu usava processos (não há muita sobrecarga de troca de contexto no Linux, mas proteção de memória devido à MMU), mas processa se precisar de uma classe de agendador em tempo real, que é uma xícara de chá diferente.
Por que você acha que os threads têm um ganho de desempenho tão grande no Linux? Você tem algum dado para isso ou é apenas um mito?
fonte
Quão fortemente acopladas são suas tarefas?
Se eles podem viver independentemente um do outro, use processos. Se eles dependem um do outro, use threads. Dessa forma, você pode matar e reiniciar um processo ruim sem interferir na operação das outras tarefas.
fonte
Para complicar ainda mais, existe o armazenamento local de threads e a memória compartilhada do Unix.
O armazenamento local do encadeamento permite que cada encadeamento tenha uma instância separada de objetos globais. A única vez que o usei foi ao construir um ambiente de emulação no linux / windows, para código de aplicativo executado em um RTOS. No RTOS, cada tarefa era um processo com seu próprio espaço de endereço, no ambiente de emulação, cada tarefa era um encadeamento (com um espaço de endereço compartilhado). Ao usar o TLS para coisas como singletons, pudemos ter uma instância separada para cada thread, exatamente como no ambiente RTOS 'real'.
A memória compartilhada pode (obviamente) oferecer os benefícios de desempenho de ter vários processos acessando a mesma memória, mas com o custo / risco de ter que sincronizar os processos corretamente. Uma maneira de fazer isso é fazer com que um processo crie uma estrutura de dados na memória compartilhada e envie um identificador para essa estrutura por meio da comunicação tradicional entre processos (como um pipe nomeado).
fonte
No meu trabalho recente com o LINUX, uma coisa é estar ciente das bibliotecas. Se você estiver usando threads, verifique se as bibliotecas que você pode usar nos threads são seguras. Isso me queimou algumas vezes. Nota-se libxml2 não é seguro para threads imediatamente. Ele pode ser compilado com thread safe, mas não é isso que você obtém com a instalação do aptitude.
fonte
Eu teria que concordar com o que você está ouvindo. Quando avaliamos nosso cluster (
xhpl
e assim por diante), sempre obtemos desempenho significativamente melhor com processos sobre threads.</anecdote>
fonte
A decisão entre encadeamento / processo depende um pouco do que você usará. Um dos benefícios de um processo é que ele possui um PID e pode ser eliminado sem também encerrar o pai.
Para um exemplo do mundo real de um servidor Web, o apache 1.3 costumava suportar apenas vários processos, mas no 2.0 eles adicionaram uma abstração para que você pudesse alternar entre eles. Comentários parece que concordam que os processos são mais robustos, mas tópicos podem dar um pouco melhor desempenho (exceto para as janelas onde o desempenho para processos suga e você só quer usar threads).
fonte
Na maioria dos casos, eu preferiria processos sobre threads. os encadeamentos podem ser úteis quando você possui uma tarefa relativamente menor (sobrecarga do processo >> tempo gasto por cada unidade de tarefa dividida) e há necessidade de compartilhamento de memória entre eles. Pense em uma grande variedade. Além disso (offtopic), observe que, se a utilização da sua CPU for 100% ou próxima dela, não haverá benefícios no multithreading ou no processamento. (de fato, vai piorar)
fonte
Threads -> Threads compartilha um espaço de memória, é uma abstração da CPU, é leve. Processos -> Processos têm seu próprio espaço de memória, é uma abstração de um computador. Para paralelizar tarefas, você precisa abstrair uma CPU. No entanto, as vantagens de usar um processo sobre um encadeamento são segurança, estabilidade, enquanto um encadeamento usa menos memória que o processo e oferece menor latência. Um exemplo em termos de web seria chrome e firefox. No caso do Chrome, cada guia é um novo processo, portanto, o uso de memória do chrome é maior que o firefox, enquanto a segurança e a estabilidade oferecidas são melhores que o firefox. A segurança aqui fornecida pelo chrome é melhor, pois cada guia é um novo processo. Uma guia diferente não pode bisbilhotar o espaço de memória de um determinado processo.
fonte
Acho que todo mundo fez um ótimo trabalho respondendo à sua pergunta. Estou apenas adicionando mais informações sobre thread versus processo no Linux para esclarecer e resumir algumas das respostas anteriores no contexto do kernel. Portanto, minha resposta está relacionada ao código específico do kernel no Linux. De acordo com a documentação do Linux Kernel, não há distinção clara entre encadeamento e processo, exceto que o encadeamento usa espaço de endereço virtual compartilhado, diferente do processo. Observe também que o kernel do Linux usa o termo "tarefa" para se referir ao processo e ao encadeamento em geral.
"Não há estruturas internas implementando processos ou threads; em vez disso, existe uma estrutura task_struct que descreve uma unidade de agendamento abstrata chamada task"
Também de acordo com Linus Torvalds, você NÃO deve pensar em processo versus encadeamento e porque é muito limitante e a única diferença é COE ou Contexto de Execução em termos de "separar o espaço de endereço do pai" ou o espaço de endereço compartilhado. De fato, ele usa um exemplo de servidor da Web para mostrar seu ponto aqui (o que recomenda a leitura).
Crédito total para documentação do kernel Linux
fonte
Se precisar compartilhar recursos, você realmente deve usar threads.
Considere também o fato de que as alternâncias de contexto entre encadeamentos são muito menos caras que as alternâncias de contexto entre processos.
Não vejo razão para usar explicitamente processos separados, a menos que você tenha um bom motivo para fazê-lo (segurança, testes de desempenho comprovados, etc ...)
fonte