thread_local在C ++ 11中意味着什么?

我对C ++ 11中的thread_local的描述感到困惑。 我的理解是,每个线程都有一个函数中局部variables的唯一副本。 全局/静态variables可以被所有线程访问(可能使用locking同步访问)。 thread_localvariables对所有线程都是可见的,但是只能由定义它们的线程修改。 这是对的吗?

线程本地存储持续时间是一个术语,用来表示看似全局或静态存储持续时间的数据(从使用它的函数的angular度来看),但实际上每个线程有一个副本。

它增加了当前的自动(在块/函数中存在),静态(存在于程序持续时间)和dynamic(存在于分配和释放之间的堆中)。

线程本地的东西在线程创build时被创build,并在线程停止时被处理。

下面是一些例子。

考虑一个随机数发生器,其中的种子必须在每个线程的基础上进行维护。 使用线程本地种子意味着每个线程都得到自己的随机数字序列,独立于其他线程。

如果你的种子是随机函数中的一个局部variables,每次调用它时都会被初始化,每次给你相同的数字。 如果是全球性的,线程会干扰彼此的序列。

另一个例子是像strtok这样的令牌化状态存储在线程特定的基础上。 这样,一个线程可以确保其他线程不会搞砸它的标记化工作,同时仍然能够通过对strtok多次调用来维护状态 – 这基本上呈现strtok_r (线程安全版本)冗余。

这两个例子都允许线程局部variables存在使用它的函数中。 在预线程代码中,它只是一个函数内的静态存储持续时间variables。 对于线程,这被修改为线程本地存储持续时间。

再举一个例子,就像errno 。 你不希望单独的线程修改errno后,你的一个调用失败,但在你可以检查variables之前,但你只需要每个线程一个副本。

这个网站有不同的存储时间说明符的合理描述。

当你声明一个variablesthread_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_localvariables的地址,那么你只是有一个普通的指针,一个普通的对象,你可以自由地在线程之间传递。 例如

 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的地址被传递给了线程函数,所以即使它是thread_local ,属于主线程的i的副本也可以被分配。 这个程序将输出“42”。 如果你这样做的话,那么你需要注意*p在它所属的线程已经退出之后不能被访问,否则你会得到一个悬挂的指针和未定义的行为,就像其他情况下被指向的对象被销毁一样。

thread_localvariables在“首次使用之前”被初始化,所以如果它们从未被给定的线程触及,那么它们不一定被初始化。 这是为了让编译器避免在程序中为完全自包含的线程构造每个thread_localvariables,并且不会触及任何线程。 例如

 struct my_class{ my_class(){ std::cout<<"hello"; } ~my_class(){ std::cout<<"goodbye"; } }; void f(){ thread_local my_class; } void do_nothing(){} int main(){ std::thread t1(do_nothing); t1.join(); } 

在这个程序中有2个线程:主线程和手动创build的线程。 线程既不调用f ,也不使用thread_local对象。 因此没有指定编译器是否构造my_class或2个实例,输出可能是“”,“hellohellogoodbyegoodbye”或“hellogoodbye”。

线程本地存储在静态(=全局)存储的每个方面,只是每个线程都有一个单独的对象副本。 对象的生存时间从线程启动(对于全局variables)或初始化(对于块本地静态)开始,并在线程结束时(即,当调用join()时结束)。

因此,只有被声明为staticvariables可以被声明为thread_local ,也就是全局variables(更确切地说:variables“在命名空间范围”),静态类成员和块静态variables(在这种情况下, static是隐含的)。

举个例子,假设你有一个线程池,并想知道你的工作负载是如何平衡的:

 thread_local Counter c; void do_work() { c.increment(); // ... } int main() { std::thread t(do_work); // your thread-pool would go here t.join(); } 

这将打印线程使用统计信息,例如与这样的实现:

 struct Counter { unsigned int c = 0; void increment() { ++c; } ~Counter() { std::cout << "Thread #" << std::this_thread::id() << " was called " << c << " times" << std::endl; } };