C++ 在C++;11?
我对C++11中的C++ 在C++;11?,c++,multithreading,c++11,thread-local,thread-local-storage,C++,Multithreading,C++11,Thread Local,Thread Local Storage,我对C++11中的thread\u local的描述感到困惑。我的理解是,每个线程在函数中都有唯一的局部变量副本。所有线程都可以访问全局/静态变量(可能使用锁进行同步访问)。而thread\u local变量对所有线程都可见,但只能由定义它们的线程修改?对吗 线程本地存储持续时间是一个术语,用于指看起来是全局或静态存储持续时间的数据(从使用它的函数的角度来看),但实际上,每个线程有一个副本 它添加到当前自动(在块/函数期间存在)、静态(在程序持续时间内存在)和动态(在分配和释放之间的堆上存在)
thread\u local
的描述感到困惑。我的理解是,每个线程在函数中都有唯一的局部变量副本。所有线程都可以访问全局/静态变量(可能使用锁进行同步访问)。而thread\u local
变量对所有线程都可见,但只能由定义它们的线程修改?对吗 线程本地存储持续时间是一个术语,用于指看起来是全局或静态存储持续时间的数据(从使用它的函数的角度来看),但实际上,每个线程有一个副本
它添加到当前自动(在块/函数期间存在)、静态(在程序持续时间内存在)和动态(在分配和释放之间的堆上存在)
线程本地的东西在线程创建时就已经存在,并在线程停止时被处理掉
下面是一些例子
设想一个随机数生成器,其中种子必须基于每个线程进行维护。使用线程本地种子意味着每个线程获得自己的随机数序列,独立于其他线程
如果种子是随机函数中的一个局部变量,那么每次调用它时它都会被初始化,每次都给你相同的数字。如果它是一个全局线程,线程将相互干扰序列
另一个示例类似于strtok
,其中标记化状态存储在特定于线程的基础上。这样,单个线程可以确保其他线程不会破坏其标记化工作,同时仍然能够在多次调用strtok
时保持状态-这基本上使strtok\r
(线程安全版本)变得多余
这两个示例都允许线程局部变量存在于使用它的函数中。在预线程化代码中,它只是函数中的一个静态存储持续时间变量。对于线程,修改为线程本地存储持续时间
另一个例子是类似于errno
。您不希望在一个调用失败后,但在检查变量之前,单独的线程修改errno
,但您只希望每个线程有一个副本
对不同的存储持续时间说明符有合理的描述。线程本地存储在各个方面都与静态(=全局)存储类似,只是每个线程都有对象的单独副本。对象的生命周期从线程开始(对于全局变量)或第一次初始化(对于块局部静态)开始,到线程结束(即调用
join()
时)结束
因此,只有也可以声明为静态
的变量可以声明为线程_局部
,即全局变量(更准确地说,变量“在命名空间范围”)、静态类成员和块静态变量(在这种情况下,隐含了静态
)
例如,假设您有一个线程池,并且想知道您的工作负载平衡得有多好:
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;
}
};
结构计数器
{
无符号整数c=0;
void increment(){++c;}
~Counter()
{
std::cout当您声明一个变量thread\u 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;
}
由于i
的地址被传递给线程函数,因此属于主线程的i
副本可以被分配给,即使它是thread\u local
。此程序将因此输出“42”。如果执行此操作,则需要注意它所属的线程退出后,不会访问*p
,否则会出现悬空指针和未定义的行为,就像破坏指向对象的任何其他情况一样
thread\u local
变量在“首次使用前”初始化,因此,如果给定线程从未接触过它们,那么它们就不一定会被初始化。这是为了让编译器避免为一个完全独立且不接触任何线程的线程构造程序中的每个thread\u 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();
}
struct my_类{
我的班级(){
std::coutUsing thread local无法解决strtok
的问题strtok
在单线程环境中是不平衡的。抱歉,让我重新表述一下。它不会给strtok带来任何新问题:-)实际上,r
代表“重新进入”,这与线程安全无关。确实,您可以使用线程本地存储使某些东西以线程安全方式工作,但不能使它们可重入。在单线程环境中,函数只有在它们是调用图中循环的一部分时才需要重入。叶函数(不调用其他函数的函数)根据定义,它不是一个循环的一部分,而且没有很好的理由说明strtok
应该调用其他函数。这会把事情搞砸:而(something){char*next=strtok(无论什么);someFunction(next);//someFunction调用strtok}
我认为需要注意的是,变量的线程本地副本是一个新初始化的变量副本
调用threadFunc
的开头,则输出将是0304029
或02
、03
和04
对的其他排列。也就是说,即使在创建线程之前将9分配给i
,线程也会获得新构造的i
副本,其中i=0
。如果i
被分配了thread\u local int i=random\u integer()
,则e
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();
}