Когда вы объявляете переменную, thread_local
каждый поток имеет свою собственную копию. Когда вы обращаетесь к нему по имени, используется копия, связанная с текущим потоком. например
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;
}
Этот код выведет «2349», «3249», «4239», «4329», «2439» или «3429», но ничего больше. У каждого потока есть своя собственная копия i
, которая назначается, увеличивается и затем печатается. Поток работаетmain
также есть собственная копия, которая назначается в начале, а затем остается неизменной. Эти копии полностью независимы и имеют разные адреса.
В этом отношении особенным является только имя - если вы берете адрес thread_local
переменной, то у вас просто есть обычный указатель на нормальный объект, который вы можете свободно передавать между потоками. например
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;
}
Поскольку адрес i
передается функции потока, то копия, i
принадлежащая основному потоку, может быть назначена, даже если это так thread_local
. Таким образом, эта программа выведет «42». Если вы это сделаете, вам нужно позаботиться о том, чтобы к *p
ним не было доступа после выхода из потока, которому он принадлежит, иначе вы получите висящий указатель и неопределенное поведение, как и в любом другом случае, когда объект, на который указывает, уничтожается.
thread_local
переменные инициализируются «перед первым использованием», поэтому, если данный поток никогда не касается их, то они не обязательно когда-либо инициализируются. Это позволяет компиляторам избегать создания каждой thread_local
переменной в программе для потока, который является полностью автономным и не затрагивает ни одну из них. например
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();
}
В этой программе есть 2 потока: основной поток и поток, созданный вручную. Ни один из потоков не вызывает f
, поэтому thread_local
объект никогда не используется. Поэтому не определено, будет ли компилятор создавать 0, 1 или 2 экземпляра my_class
, а вывод может быть «», «hellohellogoodbyegoodbye» или «hellogoodbye».
strtok
.strtok
не работает даже в однопоточной среде.