【问题标题】:Are C++11 thread_local variables automatically static?C ++ 11 thread_local变量是否自动静态?
【发布时间】:2014-05-12 17:29:16
【问题描述】:

这两个代码段有区别吗:

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,而不用担心别的?这种方法会适得其反吗?

【问题讨论】:

  • 拥有thread_local 局部变量毫无意义……每个线程都有自己的调用堆栈。
  • 最初编写了几个 C 函数来返回静态或全局变量的地址。后来发现这在多线程应用程序(例如 errno、localtime)中使用时会导致模糊的错误。此外,当从多个线程调用函数或必须在许多调用对象和方法之间传递线程上下文对象时,使用互斥锁保护共享变量有时是非常有害的。线程本地变量解决这些和其他问题。
  • @Konrad Rudolph 仅将局部变量声明为 static 而不是 static thread_local 不会为每个线程初始化一个变量实例。
  • @davide 这不是重点,无论是我还是 OP。我们不是在谈论staticstatic thread_local,而是在谈论autothread_local,使用auto 的前C++11 含义(即自动存储)。
  • 另见How to define thread-local local static variables?。快速语言律师说明... Microsoft 和 TLS 支持在 Vista 前后发生了变化;见Thread Local Storage (TLS)。该更改会影响 Singleton 之类的内容,可能适用也可能不适用。如果您使用的是 abondware 软件模型,那么您可能会没事的。如果您乐于支持多种编译器和平台,那么您可能需要注意它。

标签: c++ c++11 thread-local-storage


【解决方案1】:

根据 C++ 标准

当 thread_local 应用于块范围的变量时 storage-class-specifier static 隐含如果它没有出现 明确的

所以意思是这个定义

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 关键字声明的变量都有线程 储存期限。这些实体的存储将持续到 创建它们的线程的持续时间。有一个与众不同的 每个线程的对象或引用,并且使用声明的名称是指 与当前线程关联的实体

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

【讨论】:

  • @Deduplicator 块范围变量没有链接。所以你的简历是错误的。正如我在帖子中所写,它们具有线程存储持续时间。它实际上与静态存储时长相同,但适用于每个线程。
  • 如果你添加了 extern,你是在做一个声明而不是一个定义。那么?
  • @Deduplicator 所以说块作用域变量的定义没有联系。
  • 我刚刚在 VS 2013 中尝试过,它大喊“TL 变量无法动态初始化”。我很困惑。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    当与thread_local 一起使用时,static 隐含在块作用域中(参见@Vlad 的回答),需要类成员;我猜,意思是命名空间范围的链接。

    每 9.2/6:

    在类定义中, 除非也声明为静态成员,否则不得使用 thread_local 存储类说明符声明成员

    回答原来的问题:

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

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

    这两个代码段有区别吗:

    没有。

    【讨论】:

      【解决方案4】:

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

      当您声明一个静态变量时,该变量只有一个实例。编译器/运行时系统保证它会在你实际使用它之前的某个时间为你初始化,而没有指定确切的时间(这里省略了一些细节。)

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

      static X * pointer = new X;
      

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

      当您声明一个变量线程本地时,可能有许多变量实例。您可以将它们视为在由 thread-id 索引的地图中。这意味着每个线程都可以看到自己的变量副本。

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

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

      【讨论】:

      • @Etherealone:很有趣。哪些具体信息?可以提供参考吗?
      • stackoverflow.com/a/8102145/1576556 。如果我没记错的话,Wikipedia C++11 文章中提到了它。
      • 然而,静态对象首先被初始化,然后被复制分配。所以我也有点不清楚线程安全初始化是否包括完整的表达式。但它可能确实如此,否则它不会被认为是线程安全的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-19
      相关资源
      最近更新 更多