C++11 中的 thread_local 是什么意思?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11983875/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What does the thread_local mean in C++11?
提问by polapts
I am confused with the description of thread_local
in C++11. My understanding is, each thread has unique copy of local variables in a function. The global/static variables can be accessed by all the threads (possibly synchronized access using locks). And the thread_local
variables are visible to all the threads but can only modified by the thread for which they are defined? Is it correct?
我对thread_local
C++11 中的描述感到困惑。我的理解是,每个线程在函数中都有唯一的局部变量副本。所有线程都可以访问全局/静态变量(可能使用锁进行同步访问)。并且thread_local
变量对所有线程都是可见的,但只能由为其定义的线程修改?这是正确的吗?
回答by paxdiablo
Thread-local storage duration is a term used to refer to data that is seemingly global or static storage duration (from the viewpoint of the functions using it) but in actual fact, there is one copy per thread.
线程局部存储时长是一个术语,用于指数据看似全局或静态的存储时长(从使用它的函数的角度来看),但实际上每个线程都有一个副本。
It adds to the current automatic (exists during a block/function), static (exists for the program duration) and dynamic (exists on the heap between allocation and deallocation).
它增加了当前的自动(存在于块/函数期间)、静态(存在于程序持续时间)和动态(存在于分配和释放之间的堆上)。
Something that is thread-local is brought into existence at thread creation and disposed of when the thread stops.
线程本地的东西在线程创建时就存在,并在线程停止时处理掉。
Some examples follow.
下面是一些例子。
Think of a random number generator where the seed must be maintained on a per-thread basis. Using a thread-local seed means that each thread gets its own random number sequence, independent of other threads.
考虑一个随机数生成器,其中必须在每个线程的基础上维护种子。使用线程本地种子意味着每个线程获得自己的随机数序列,独立于其他线程。
If your seed was a local variable within the random function, it would be initialised every time you called it, giving you the same number each time. If it was a global, threads would interfere with each other's sequences.
如果您的种子是随机函数中的局部变量,则每次调用它时都会对其进行初始化,每次都给您相同的数字。如果它是全局的,线程会干扰彼此的序列。
Another example is something like strtok
where the tokenisation state is stored on a thread-specific basis. That way, a single thread can be sure that other threads won't screw up its tokenisation efforts, while still being able to maintain state over multiple calls to strtok
- this basically renders strtok_r
(the thread-safe version) redundant.
另一个例子类似于strtok
标记化状态存储在线程特定的基础上。这样,单个线程可以确保其他线程不会破坏其标记化工作,同时仍然能够通过多次调用来维护状态strtok
- 这基本上使strtok_r
(线程安全版本)变得多余。
Both these examples allow for the thread local variable to exist withinthe function that uses it. In pre-threaded code, it would simply be a static storage duration variable within the function. For threads, that's modified to thread local storage duration.
这两个示例都允许线程局部变量存在于使用它的函数中。在预线程代码中,它只是函数内的静态存储持续时间变量。对于线程,修改为线程本地存储持续时间。
Yet another example would be something like errno
. You don't want separate threads modifying errno
after one of your calls fails but before you can check the variable, and yet you only want one copy per thread.
另一个例子是类似errno
. 您不希望errno
在您的一个调用失败后但在检查变量之前修改单独的线程,但您只需要每个线程一个副本。
This sitehas a reasonable description of the different storage duration specifiers.
该站点对不同的存储期限说明符进行了合理的描述。
回答by Anthony Williams
When you declare a variable thread_local
then each thread has its own copy. When you refer to it by name, then the copy associated with the current thread is used. e.g.
当您声明一个变量时,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;
}
This code will output "2349", "3249", "4239", "4329", "2439" or "3429", but never anything else. Each thread has its own copy of i
, which is assigned to, incremented and then printed. The thread running main
also has its own copy, which is assigned to at the beginning and then left unchanged. These copies are entirely independent, and each has a different address.
此代码将输出“2349”、“3249”、“4239”、“4329”、“2439”或“3429”,但不会输出任何其他内容。每个线程都有自己的 副本i
,该副本被分配、递增然后打印。运行的线程main
也有自己的副本,它在开始时被分配,然后保持不变。这些副本是完全独立的,每个副本都有不同的地址。
It is only the namethat is special in that respect --- if you take the address of a thread_local
variable then you just have a normal pointer to a normal object, which you can freely pass between threads. e.g.
这只是在这方面的特殊名称--- 如果您获取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;
}
Since the address of i
is passed to the thread function, then the copy of i
belonging to the main thread can be assigned to even though it is thread_local
. This program will thus output "42". If you do this, then you need to take care that *p
is not accessed after the thread it belongs to has exited, otherwise you get a dangling pointer and undefined behaviour just like any other case where the pointed-to object is destroyed.
由于 的地址i
传递给线程函数,因此i
即使是 ,也可以将属于主线程的副本分配给thread_local
。该程序将因此输出“42”。如果你这样做,那么你需要注意*p
在它所属的线程退出后不会被访问,否则你会得到一个悬空指针和未定义的行为,就像被指向的对象被销毁的任何其他情况一样。
thread_local
variables are initialized "before first use", so if they are never touched by a given thread then they are not necessarily ever initialized. This is to allow compilers to avoid constructing every thread_local
variable in the program for a thread that is entirely self-contained and doesn't touch any of them. e.g.
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();
}
In this program there are 2 threads: the main thread and the manually-created thread. Neither thread calls f
, so the thread_local
object is never used. It is therefore unspecified whether the compiler will construct 0, 1 or 2 instances of my_class
, and the output may be "", "hellohellogoodbyegoodbye" or "hellogoodbye".
在这个程序中有 2 个线程:主线程和手动创建的线程。两个线程都没有调用f
,因此thread_local
永远不会使用该对象。因此,未指定编译器是否会构造 0、1 或 2 个实例my_class
,并且输出可能是“”、“hellohellogoodbyegoodbye”或“hellogoodbye”。
回答by Kerrek SB
Thread-local storage is in every aspect like static (= global) storage, only that each thread has a separate copy of the object. The object's life time starts either at thread start (for global variables) or at first initialization (for block-local statics), and ends when the thread ends (i.e. when join()
is called).
线程本地存储在各个方面都类似于静态(=全局)存储,只是每个线程都有一个单独的对象副本。对象的生命周期从线程开始(对于全局变量)或第一次初始化(对于块局部静态)开始,并在线程结束时(即被join()
调用时)结束。
Consequently, only variables that could also be declared static
may be declared as thread_local
, i.e. global variables (more precisely: variables "at namespace scope"), static class members, and block-static variables (in which case static
is implied).
因此,只有也可以声明的变量static
可以声明为thread_local
,即全局变量(更准确地说:变量“在命名空间范围内”)、静态类成员和块静态变量(在这种情况下static
是隐含的)。
As an example, suppose you have a thread pool and want to know how well your work load was being balanced:
例如,假设您有一个线程池并想知道您的工作负载平衡情况:
thread_local Counter c;
void do_work()
{
c.increment();
// ...
}
int main()
{
std::thread t(do_work); // your thread-pool would go here
t.join();
}
This would print thread usage statistics, e.g. with an implementation like this:
这将打印线程使用统计信息,例如使用如下实现:
struct Counter
{
unsigned int c = 0;
void increment() { ++c; }
~Counter()
{
std::cout << "Thread #" << std::this_thread::id() << " was called "
<< c << " times" << std::endl;
}
};