Duração do armazenamento local do encadeamento é um termo usado para se referir a dados que são aparentemente globais ou estáticos (do ponto de vista das funções que o utilizam), mas, na realidade, existe uma cópia por encadeamento.
Ele adiciona ao automático atual (existe durante um bloco / função), estático (existe durante o programa) e dinâmico (existe no monte entre alocação e desalocação).
Algo local do encadeamento é criado na criação do encadeamento e descartado quando o encadeamento é interrompido.
Alguns exemplos a seguir.
Pense em um gerador de números aleatórios onde a semente deve ser mantida por thread. Usar uma semente local de encadeamento significa que cada encadeamento obtém sua própria sequência de números aleatórios, independente de outros encadeamentos.
Se sua semente fosse uma variável local dentro da função aleatória, ela seria inicializada toda vez que você a chamasse, fornecendo o mesmo número a cada vez. Se fosse global, os threads interfeririam nas seqüências um do outro.
Outro exemplo é algo como strtok
onde o estado de tokenização é armazenado em uma base específica do encadeamento. Dessa forma, um único encadeamento pode ter certeza de que outros encadeamentos não estragarão seus esforços de tokenização, enquanto ainda é capaz de manter o estado de várias chamadas para strtok
- isso basicamente torna strtok_r
redundante (a versão segura do encadeamento).
Ambos os exemplos permitem que a variável local do encadeamento exista na função que a utiliza. No código pré-encadeado, seria simplesmente uma variável estática de duração de armazenamento dentro da função. Para encadeamentos, isso é modificado para encadear a duração do armazenamento local.
Ainda outro exemplo seria algo parecido errno
. Você não deseja que threads separados sejam modificados errno
depois que uma das suas chamadas falhar, mas antes de poder verificar a variável e, no entanto, deseja apenas uma cópia por thread.
Este site tem uma descrição razoável dos diferentes especificadores de duração de armazenamento.
strtok
.strtok
está quebrado mesmo em um único ambiente de encadeamento.r
significa "reentrante", que não tem nada a ver com a segurança do thread. É verdade que você pode fazer algumas coisas funcionarem com segurança com o armazenamento local de threads, mas não pode fazê-las reentrarem.strtok
chamar outras funções.while (something) { char *next = strtok(whatever); someFunction(next); // someFunction calls strtok }
Quando você declara uma variável
thread_local
, cada thread tem sua própria cópia. Quando você se refere a ele pelo nome, a cópia associada ao encadeamento atual é usada. por exemploEsse código produzirá "2349", "3249", "4239", "4329", "2439" ou "3429", mas nunca mais nada. Cada encadeamento possui sua própria cópia
i
, atribuída, incrementada e impressa. O encadeamento em execuçãomain
também possui sua própria cópia, a qual é atribuída no início e depois deixada inalterada. Essas cópias são totalmente independentes e cada uma possui um endereço diferente.É apenas o nome que é especial a esse respeito - se você pegar o endereço de uma
thread_local
variável, basta ter um ponteiro normal para um objeto normal, que você pode passar livremente entre os threads. por exemploComo o endereço de
i
é passado para a função de encadeamento, a cópia dei
pertencer ao encadeamento principal pode ser atribuída, mesmo que sejathread_local
. Este programa produzirá, portanto, "42". Se você fizer isso, precisará tomar cuidado para que*p
não seja acessado após a saída do thread ao qual pertence, caso contrário, você receberá um ponteiro oscilante e um comportamento indefinido, como em qualquer outro caso em que o objeto apontado seja destruído.thread_local
As variáveis são inicializadas "antes do primeiro uso"; portanto, se nunca forem tocadas por um determinado encadeamento, nunca serão necessariamente inicializadas. Isso permite que os compiladores evitem construir todas asthread_local
variáveis do programa para um thread que seja totalmente independente e não toque em nenhuma delas. por exemploNeste programa, existem 2 threads: o thread principal e o thread criado manualmente. Nenhum thread é chamado
f
, portanto othread_local
objeto nunca é usado. Portanto, não é especificado se o compilador construirá 0, 1 ou 2 instânciasmy_class
e a saída pode ser "", "hellohellogoodbyegoodbye" ou "hellogoodbye".fonte
g()
chamada para o iníciothreadFunc
, em seguida, a saída será0304029
ou alguma outra permutação dos pares02
,03
e04
. Ou seja, mesmo que 9 seja atribuídoi
antes da criação dos threads, os threads obtêm uma cópia recém-construída dei
ondei=0
. Sei
for atribuído comthread_local int i = random_integer()
, cada thread obterá um novo número inteiro aleatório.02
,03
,04
, pode haver outras seqüências como020043
O armazenamento local do encadeamento é, em todos os aspectos, como estático (= global), apenas que cada encadeamento tenha uma cópia separada do objeto. O tempo de vida do objeto começa no início do encadeamento (para variáveis globais) ou na primeira inicialização (para estática local do bloco) e termina quando o encadeamento termina (ou seja, quando
join()
é chamado).Consequentemente, apenas variáveis que também podem ser declaradas
static
podem ser declaradas comothread_local
, isto é, variáveis globais (mais precisamente: variáveis "no escopo do espaço de nome"), membros de classe estática e variáveis estáticas de bloco (nesse caso,static
implícitas).Como exemplo, suponha que você tenha um conjunto de encadeamentos e queira saber o quão bem sua carga de trabalho estava sendo equilibrada:
Isso imprimiria estatísticas de uso de threads, por exemplo, com uma implementação como esta:
fonte