Quando dichiari una variabile, thread_local
ogni thread ha la sua copia. Quando ti riferisci ad esso per nome, viene utilizzata la copia associata al thread corrente. per esempio
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;
}
Questo codice genererà "2349", "3249", "4239", "4329", "2439" o "3429", ma mai nient'altro. Ogni thread ha una propria copia di i
, che viene assegnata, incrementata e quindi stampata. Il thread è in esecuzionemain
ha anche una sua copia, che viene assegnata all'inizio e quindi lasciata invariata. Queste copie sono completamente indipendenti e ognuna ha un indirizzo diverso.
È solo il nome che è speciale al riguardo --- se prendi l'indirizzo di una thread_local
variabile allora hai solo un normale puntatore a un oggetto normale, che puoi passare liberamente tra i thread. per esempio
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;
}
Poiché l'indirizzo di i
viene passato alla funzione thread, è i
possibile assegnare la copia di appartenenza al thread principale anche se lo è thread_local
. Questo programma genererà quindi "42". Se lo fai, devi fare attenzione a *p
non accedere dopo che il thread a cui appartiene è uscito, altrimenti otterrai un puntatore penzolante e un comportamento indefinito proprio come qualsiasi altro caso in cui l'oggetto puntato viene distrutto.
thread_local
le variabili vengono inizializzate "prima del primo utilizzo", quindi se non vengono mai toccate da un determinato thread, non vengono necessariamente inizializzate. Questo per consentire ai compilatori di evitare di costruire ogni thread_local
variabile nel programma per un thread completamente autonomo e che non tocchi nessuno di essi. per esempio
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();
}
In questo programma ci sono 2 thread: il thread principale e il thread creato manualmente. Nessuno dei thread chiama f
, quindi l' thread_local
oggetto non viene mai utilizzato. Non è quindi specificato se il compilatore costruirà 0, 1 o 2 istanze di my_class
e l'output potrebbe essere "", "hellohellogoodbyegoodbye" o "hellogoodbye".
strtok
.strtok
viene interrotto anche in un singolo ambiente thread.