Kiedy deklarujesz zmienną, thread_local
każdy wątek ma swoją własną kopię. Kiedy odnosisz się do niego po nazwie, używana jest kopia skojarzona z bieżącym wątkiem. na przykład
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;
}
Ten kod zwróci „2349”, „3249”, „4239”, „4329”, „2439” lub „3429”, ale nigdy więcej. Każdy wątek ma swoją własną kopię i
, do której jest przypisywana, zwiększana, a następnie drukowana. Działający wątek main
ma również swoją własną kopię, która jest przypisywana na początku, a następnie pozostawiana bez zmian. Kopie te są całkowicie niezależne i każda ma inny adres.
Jedynie nazwa jest pod tym względem wyjątkowa - jeśli bierzesz adres thread_local
zmiennej, masz po prostu normalny wskaźnik do normalnego obiektu, który możesz swobodnie przekazywać między wątkami. na przykład
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;
}
Ponieważ adres i
jest przekazywany do funkcji i
wątku, można przypisać kopię przynależności do wątku głównego, mimo że tak jest thread_local
. Ten program zwróci zatem „42”. Jeśli to zrobisz, musisz uważać, aby *p
nie uzyskać dostępu po wyjściu wątku, do którego należy, w przeciwnym razie otrzymasz wiszący wskaźnik i niezdefiniowane zachowanie, tak jak w każdym innym przypadku, gdy wskazany obiekt zostanie zniszczony.
thread_local
zmienne są inicjalizowane „przed pierwszym użyciem”, więc jeśli nigdy nie są dotykane przez dany wątek, to niekoniecznie są one nigdy inicjalizowane. Ma to pozwolić kompilatorom na uniknięcie konstruowania każdej thread_local
zmiennej w programie dla wątku, który jest całkowicie niezależny i nie dotyka żadnego z nich. na przykład
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();
}
W tym programie są 2 wątki: wątek główny i wątek utworzony ręcznie. Żaden wątek nie jest wywoływany f
, więc thread_local
obiekt nigdy nie jest używany. W związku z tym nie jest określone, czy kompilator skonstruuje 0, 1 czy 2 wystąpienia my_class
, a wyjściem może być „”, „hellohellogoodbyegoodbye” lub „hellogoodbye”.
strtok
.strtok
jest zepsuty nawet w środowisku jednowątkowym.