Lorsque vous déclarez une variable, thread_local
chaque thread a sa propre copie. Lorsque vous y faites référence par son nom, la copie associée au thread actuel est utilisée. par exemple
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;
}
Ce code affichera "2349", "3249", "4239", "4329", "2439" ou "3429", mais jamais rien d'autre. Chaque fil a sa propre copie de i
, qui est assignée, incrémentée puis imprimée. Le fil en coursmain
également sa propre copie, qui est affectée au début et laissée inchangée. Ces copies sont entièrement indépendantes et ont chacune une adresse différente.
C'est seulement le nom qui est spécial à cet égard --- si vous prenez l'adresse d'une thread_local
variable, vous avez juste un pointeur normal vers un objet normal, que vous pouvez passer librement entre les threads. par exemple
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;
}
Depuis l'adresse de i
est passée à la fonction de thread, alors la copie de l' i
appartenance au thread principal peut être affectée même si elle l'est thread_local
. Ce programme affichera donc "42". Si vous faites cela, vous devez faire attention à *p
ne pas y accéder après la sortie du thread auquel il appartient, sinon vous obtenez un pointeur suspendu et un comportement indéfini comme dans tout autre cas où l'objet pointé est détruit.
thread_local
les variables sont initialisées "avant la première utilisation", donc si elles ne sont jamais touchées par un thread donné, elles ne sont pas nécessairement jamais initialisées. Cela permet aux compilateurs d'éviter de construire toutes les thread_local
variables du programme pour un thread entièrement autonome et qui ne touche à aucune d'elles. par exemple
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();
}
Dans ce programme, il y a 2 threads: le thread principal et le thread créé manuellement. Aucun des threads n'appelle f
, donc l' thread_local
objet n'est jamais utilisé. Il n'est donc pas spécifié si le compilateur construira 0, 1 ou 2 instances de my_class
, et la sortie peut être "", "hellohellogoodbyegoodbye" ou "hellogoodbye".
strtok
.strtok
est cassé même dans un environnement à thread unique.