Os threads têm uma pilha distinta?

114

Pelo que eu sei, cada thread obtém uma pilha distinta quando o thread é criado pelo sistema operacional. Eu me pergunto se cada thread tem um heap distinto para si também?

Martin Thoma
fonte
sim, windows e linux, biblioteca c
3
Agradável. 1 mantenha essas questões fundamentais chegando.

Respostas:

128

Não. Todos os threads compartilham um heap comum.

Cada thread tem uma pilha privada , da qual pode adicionar e remover itens rapidamente. Isso torna a memória baseada em pilha rápida, mas se você usar muita memória de pilha, como ocorre na recursão infinita, você obterá um estouro de pilha.

Como todos os threads compartilham o mesmo heap, o acesso ao alocador / desalocador deve ser sincronizado. Existem vários métodos e bibliotecas para evitar a contenção do alocador .

Alguns idiomas permitem que você crie pools privados de memória, ou heaps individuais, que você pode atribuir a um único thread.

brianegge
fonte
5
Normalmente, os threads compartilham recursos, como memória, portanto, qualquer implementação de thread não braindead compartilharia o heap.
R. Martinho Fernandes
10
A principal razão de cada thread ter sua própria pilha é para que a thread possa realmente fazer algo (como chamar funções) ...
Edmund
3
Cada thread tem uma pilha separada, mas não é necessariamente 'privada'. Outros tópicos geralmente têm permissão para acessá-lo.
zch
you will get a stack overflow.Um estouro de pilha em Stack Overflow!
John Strood
2
@crisron É possível configurar um heap separado para cada thread, mas se você fizer isso em vez de usar o heap compartilhado padrão, torna-se difícil, por exemplo, o thread A alocar um buffer, preenchê-lo com dados e passá-lo para o thread B , e fazer com que o thread B use os dados e depois libere o buffer (porque o thread B não tem acesso ao heap do thread A, o thread B não pode liberar o buffer; o melhor que o thread B poderia fazer é passar o buffer de volta para o thread A novamente e faça com que o segmento A o solte).
Jeremy Friesner
9

Por padrão, C tem apenas um único heap.

Dito isso, alguns alocadores com reconhecimento de thread particionarão o heap de modo que cada thread tenha sua própria área para alocar. A ideia é que isso deve melhorar a escala do heap.

Um exemplo de tal pilha é Hoard .

R Samuel Klatchko
fonte
Por padrão, C e C ++ não têm vários threads. A especificação c ++ de 2003 pelo menos não faz concessões para threads em seu projeto de máquina virtual, portanto, threads, em c ++, são definidos pela implementação.
Chris Becke
Mesmo se encadeamentos diferentes tiverem áreas diferentes para alocar no heap, eles ainda podem ver os dados alocados por outro encadeamento, portanto, os encadeamentos ainda compartilham o mesmo heap.
Ken Bloom
1
Atualização: a partir do C ++ 11, os threads não são mais definidos pela implementação.
Michael Dorst
5

Depende do SO. O tempo de execução c padrão em windows e unices usa um heap compartilhado entre threads. Isso significa bloquear todos os malloc / free.

No Symbian, por exemplo, cada thread vem com seu próprio heap, embora os threads possam compartilhar ponteiros para dados alocados em qualquer heap. O design do Symbian é melhor na minha opinião, uma vez que não apenas elimina a necessidade de bloqueio durante a alocação / liberação, mas também incentiva a especificação limpa de propriedade de dados entre threads. Também nesse caso, quando um thread morre, ele leva todos os objetos alocados junto com ele - ou seja, ele não pode vazar objetos que alocou, o que é uma propriedade importante em dispositivos móveis com memória restrita.

Erlang também segue um design semelhante, onde um "processo" atua como uma unidade de coleta de lixo. Todos os dados são comunicados entre processos por cópia, exceto para blobs binários que são contados por referência (eu acho).

Srikumar
fonte
3

Cada thread tem sua própria pilha e pilha de chamadas.

Cada thread compartilha o mesmo heap.

tsalter
fonte
3

Depende do que exatamente você quer dizer com "amontoar".

Todos os threads compartilham o espaço de endereço, portanto, os objetos alocados por heap são acessíveis de todos os threads. Tecnicamente, as pilhas também são compartilhadas neste sentido, ou seja, nada impede que você acesse a pilha de outra thread (embora isso quase nunca faça sentido).

Por outro lado, existem estruturas de heap usadas para alocar memória. É aí que toda a contabilidade para alocação de memória heap é feita. Essas estruturas são sofisticadamente organizadas para minimizar a contenção entre os threads - portanto, alguns threads podem compartilhar uma estrutura de heap (uma arena) e alguns podem usar arenas distintas.
Consulte o thread a seguir para obter uma explicação excelente dos detalhes: Como o malloc funciona em um ambiente multithread?

VladV
fonte
1

Normalmente, os threads compartilham o heap e outros recursos, no entanto, há construções semelhantes a threads que não compartilham. Entre essas construções semelhantes a thread estão os processos leves de Erlang e os processos full-on do UNIX (criados com uma chamada para fork()). Você também pode estar trabalhando em simultaneidade multi-máquina; nesse caso, suas opções de comunicação entre threads são consideravelmente mais limitadas.

Ken Bloom
fonte
Achei que fork era mais como criar um novo processo que apenas copia os dados para um novo local de memória.
Jason Tholstrup
2
fork () pode servir em muitos casos de uso onde os threads também podem ser usados. Devido ao copy-on-write, não há diferença significativa de custo em sistemas Unix. O caso de uso típico é aquele em que o trabalhador é autônomo (como um servidor da web) do restante do serviço. Outra possibilidade é comunicar-se por meio de stdin / out com o thread / programa principal. fork () é forte no Unix, enquanto outras plataformas como o Windows preferem threading. A principal razão provavelmente é que usar fork () é muito mais simples e seguro e o Unix tem essa filosofia de simplicidade. Veja, por exemplo, servidor web apache, com sua lenta transição para threads.
ypnos
1

De um modo geral, todos os threads usam o mesmo espaço de endereço e, portanto, geralmente têm apenas um heap.

No entanto, pode ser um pouco mais complicado. Você pode estar procurando por Thread Local Storage (TLS), mas ele armazena apenas valores únicos.

Específico do Windows: o espaço TLS pode ser alocado usando TlsAlloc e liberado usando TlsFree (Visão geral aqui ). Novamente, não é um heap, apenas DWORDs.

Estranhamente, o Windows oferece suporte a vários Heaps por processo. Pode-se armazenar o identificador do Heap em TLS. Então você teria algo como um "Thread-Local Heap". No entanto, apenas o identificador não é conhecido pelas outras threads, elas ainda podem acessar sua memória usando ponteiros, pois ainda é o mesmo espaço de endereço.

EDITAR : Alguns alocadores de memória (especificamente o jemalloc no FreeBSD) usam TLS para atribuir "arenas" às threads. Isso é feito para otimizar a alocação de vários núcleos, reduzindo a sobrecarga de sincronização.

Meinersbur
fonte
> "Estranhamente, o Windows suporta vários Heaps por processo.", Não é nada estranho, pode-se usar diferentes heaps para diferentes tipos de alocações, apenas adiciona mais flexibilidade. É claro que você sempre pode usar o VirtualAlloc e construir seu próprio heap da maneira que quiser.
1

No sistema operacional FreeRTOS, as tarefas (threads) compartilham a mesma pilha, mas cada uma delas tem sua própria pilha. Isso é muito útil ao lidar com arquiteturas de RAM de baixo consumo de energia, porque o mesmo pool de memória pode ser acessado / compartilhado por vários threads, mas isso vem com um pequeno problema, o desenvolvedor precisa ter em mente que um mecanismo para sincronizar malloc e free é necessário, por isso é necessário usar algum tipo de sincronização / bloqueio de processo ao alocar ou liberar memória no heap, por exemplo, um semáforo ou um mutex.

Noreddine -Kessa
fonte