Cuando declaras una variable thread_local
, cada hilo tiene su propia copia. Cuando se refiere a él por su nombre, se utiliza la copia asociada con el hilo actual. p.ej
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;
}
Este código generará "2349", "3249", "4239", "4329", "2439" o "3429", pero nunca otra cosa. Cada hilo tiene su propia copia de i
, que se asigna, se incrementa y luego se imprime. El hilo en ejecución main
también tiene su propia copia, que se asigna al principio y luego se deja sin cambios. Estas copias son completamente independientes y cada una tiene una dirección diferente.
Es solo el nombre lo que es especial a ese respecto: si toma la dirección de una thread_local
variable, simplemente tiene un puntero normal a un objeto normal, que puede pasar libremente entre hilos. p.ej
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;
}
Dado que la dirección de i
se pasa a la función de subproceso, la copia de i
pertenencia al subproceso principal se puede asignar aunque sea thread_local
. Este programa generará así "42". Si hace esto, debe tener cuidado de que *p
no se acceda después de que el hilo al que pertenece haya salido, de lo contrario, obtendrá un puntero colgante y un comportamiento indefinido como cualquier otro caso en el que se destruye el objeto señalado.
thread_local
las variables se inicializan "antes del primer uso", por lo que si nunca son tocadas por un hilo dado, entonces no necesariamente se inicializan. Esto es para permitir que los compiladores eviten construir cada thread_local
variable en el programa para un hilo que sea completamente autónomo y no toque ninguna de ellas. p.ej
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();
}
En este programa hay 2 hilos: el hilo principal y el hilo creado manualmente. Ninguno de los hilos llama f
, por lo que el thread_local
objeto nunca se usa. Por lo tanto, no se especifica si el compilador construirá 0, 1 o 2 instancias de my_class
, y la salida puede ser "", "hellohellogoodbyegoodbye" o "hellogoodbye".
strtok
.strtok
se rompe incluso en un entorno de un solo subproceso.