C ++ 11 thread_local变量是否自动静态?


85

这两个代码段之间是否有区别:

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

背景知识:最初,我有一个STATIC向量V(用于保存一些中间值,每次输入函数时都会清除它)和一个单线程程序。我想将程序变成一个多线程程序,因此我必须摆脱这种静态修饰符。我的想法是将每个静态变量都转换为thread_local,而不用担心其他事情?这种做法会适得其反吗?


16
有一个thread_local局部变量是没有意义的开始......每个线程都有自己的调用堆栈。
Konrad Rudolph

1
最初编写了几个C函数来返回静态或全局变量的地址。后来发现这在多线程应用程序(例如errno,localtime)中使用时会导致模糊的错误。另外,有时从多个线程中调用一个函数时,使用互斥锁保护共享变量是非常有害的,或者必须在许多调用对象和方法之间传递线程上下文对象。.局部于线程的变量可以解决这些和其他问题。
edwinc

3
@Konrad Rudolph仅将局部变量声明为,static而不是static thread_local不为每个线程初始化变量的一个实例。
davide'1

1
@davide这不是我或OP的重点。使用C ++ 11之前的含义(即自动存储),我们不是在讨论staticvs static thread_local,而是在讨论autovs。thread_localauto
康拉德·鲁道夫

1
另请参阅如何定义线程局部局部静态变量?。快速的语言律师说明... Microsoft和TLS支持在Vista左右发生了变化。请参阅线程本地存储(TLS)。更改影响像Singleton一样的事情,可能适用也可能不适用。如果您使用的是abondware软件模型,则可能会没事。如果您乐于支持多个编译器和平台,则可能需要注意它。
jww

Answers:


92

根据C ++标准

将thread_local应用于块作用域的变量时,如果未明确显示存储类说明符静态变量,则将其隐含

所以这意味着这个定义

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

相当于

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

然而,一个静态变量是一样的一个thread_local变量。

1使用thread_local关键字声明的所有变量都有线程存储持续时间。这些实体的存储应在创建它们的线程的持续时间内持续。每个线程有一个不同的对象或引用,使用声明的名称是指与当前线程关联的实体

为了区分这些变量,标准引入了新的术语线程存储持续时间以及静态存储持续时间。


1
staticextern因此不意味着存储类,但只有联动,即使在内部范围thread_local变量。
Deduplicator 2014年

4
@Deduplicator块作用域变量没有链接。所以你的简历是错误的。如我在帖子中所写,它们具有线程存储持续时间。实际上,它与静态存储时间相同,但适用于每个线程。
弗拉德(Flad),来自莫斯科

1
如果添加extern,则说明是声明而不是定义。所以?
Deduplicator 2014年

1
@Deduplicator因此,这意味着块作用域变量的定义没有链接。
弗拉德(Flad)来自莫斯科

1
我只是在VS 2013中尝试过,它喊“无法动态初始化TL变量”。我很困惑
v.oddou

19

是的,“线程本地存储”与“全局”(或“静态存储”)非常相似,只不过它具有“整个线程的持续时间”而不是“整个程序的持续时间”。因此,在控件第一次通过其声明时会初始化一个块局部线程局部变量,但该变量在每个线程中分别进行初始化,并在线程结束时销毁。


6

与一起使用时thread_local,表示static为块成员(请参阅@Vlad的答案),是类成员所必需的;我想,这意味着链接名称空间范围。

根据9.2 / 6:

在类定义中,除非也声明为静态,否则不得用thread_local storage-class-specifier声明成员。

要回答原始问题:

C ++ 11 thread_local变量是否自动静态?

除了命名空间范围变量之外,别无选择。

这两个代码段之间是否有区别:

没有。


4

线程本地存储是静态的,但其行为与简单的静态存储完全不同。

当声明一个静态变量时,该变量恰好有一个实例。编译器/运行时系统保证在您实际使用它之前的某个时候为您初始化它,而无需指定确切的时间(此处省略了一些详细信息)。

C ++ 11保证此初始化将是线程安全的,但是在C ++ 11之前,不能保证此线程安全。例如

static X * pointer = new X;

如果多个线程同时命中静态初始化代码,则可能泄漏X的实例。

当您在本地声明变量线程时,该变量可能存在许多实例。您可能会认为它们位于由线程ID索引的映射中。这意味着每个线程都会看到自己的变量副本。

再次,如果变量被初始化,则编译器/运行时系统保证该初始化将在使用数据之前进行,并且初始化将针对使用该变量的每个线程进行。编译器还保证初始化将是线程安全的。

线程安全性保证意味着可以有很多幕后代码来使变量按您期望的方式运行-尤其是考虑到编译器无法提前知道确切的线程数量存在于您的程序中,其中有多少将触及线程局部变量。


@Etherealone:有趣。哪些特定信息?你能提供参考吗?
Dale Wilson

1
stackoverflow.com/a/8102145/1576556。如果我没记错的话,Wikipedia C ++ 11文章提到了它。
Etherealone 2014年

1
但是,首先将静态对象初始化,然后再分配副本。所以我也不清楚线程安全的初始化是否包含完整的表达式。但是,它可能会这样做,因为否则将不被认为是线程安全的。
Etherealone 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.