Khi bạn khai báo một biến thread_local
thì mỗi luồng có bản sao riêng. Khi bạn gọi nó theo tên, thì bản sao được liên kết với chuỗi hiện tại sẽ được sử dụng. ví dụ
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;
}
Mã này sẽ xuất ra "2349", "3249", "4239", "4329", "2439" hoặc "3429", nhưng không bao giờ có gì khác. Mỗi luồng có bản sao riêng của nó i
, được gán cho, tăng lên và sau đó được in. Các luồng đang chạy main
cũng có bản sao riêng của nó, được gán ở đầu và sau đó không thay đổi. Các bản sao này là hoàn toàn độc lập, và mỗi bản có một địa chỉ khác nhau.
Đó chỉ là cái tên đặc biệt trong khía cạnh đó --- nếu bạn lấy địa chỉ của một thread_local
biến thì bạn chỉ cần có một con trỏ bình thường đến một đối tượng bình thường, mà bạn có thể tự do chuyển qua giữa các luồng. ví dụ
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;
}
Vì địa chỉ của i
được truyền cho hàm luồng, nên bản sao i
thuộc về luồng chính có thể được gán cho mặc dù vậy thread_local
. Chương trình này do đó sẽ xuất "42". Nếu bạn làm điều này, thì bạn cần phải cẩn thận *p
không truy cập được sau khi luồng mà nó thuộc về đã thoát, nếu không bạn sẽ có một con trỏ lơ lửng và hành vi không xác định giống như bất kỳ trường hợp nào khác khi đối tượng trỏ bị phá hủy.
thread_local
các biến được khởi tạo "trước khi sử dụng lần đầu", vì vậy nếu chúng không bao giờ được chạm bởi một luồng nhất định thì chúng không nhất thiết phải được khởi tạo. Điều này là để cho phép trình biên dịch tránh xây dựng mọi thread_local
biến trong chương trình cho một luồng hoàn toàn độc lập và không chạm vào bất kỳ biến nào trong số chúng. ví dụ
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();
}
Trong chương trình này có 2 luồng: luồng chính và luồng được tạo thủ công. Không có cuộc gọi chủ đề f
, vì vậy thread_local
đối tượng không bao giờ được sử dụng. Do đó, không xác định liệu trình biên dịch sẽ xây dựng 0, 1 hoặc 2 trường hợp my_class
và đầu ra có thể là "", "hellohellogoodbyegoodbye" hoặc "hellogoodbye".
strtok
.strtok
bị hỏng ngay cả trong một môi trường luồng đơn.