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 exemplo
thread_local int i=0;
void f(int newval){
i=newval;
}
void g(){
std::cout<<i;
}
void threadfunc(int id){
f(id);
++i;
g();
}
int main(){
i=9;
std::thread t1(threadfunc,1);
std::thread t2(threadfunc,2);
std::thread t3(threadfunc,3);
t1.join();
t2.join();
t3.join();
std::cout<<i<<std::endl;
}
Esse 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ção main
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 exemplo
thread_local int i=0;
void thread_func(int*p){
*p=42;
}
int main(){
i=9;
std::thread t(thread_func,&i);
t.join();
std::cout<<i<<std::endl;
}
Como o endereço de i
é passado para a função de encadeamento, a cópia de i
pertencer ao encadeamento principal pode ser atribuída, mesmo que seja thread_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 as thread_local
variáveis do programa para um thread que seja totalmente independente e não toque em nenhuma delas. por exemplo
struct my_class{
my_class(){
std::cout<<"hello";
}
~my_class(){
std::cout<<"goodbye";
}
};
void f(){
thread_local my_class unused;
}
void do_nothing(){}
int main(){
std::thread t1(do_nothing);
t1.join();
}
Neste programa, existem 2 threads: o thread principal e o thread criado manualmente. Nenhum thread é chamado f
, portanto o thread_local
objeto nunca é usado. Portanto, não é especificado se o compilador construirá 0, 1 ou 2 instâncias my_class
e a saída pode ser "", "hellohellogoodbyegoodbye" ou "hellogoodbye".
strtok
.strtok
está quebrado mesmo em um único ambiente de encadeamento.