我的编译器是否忽略了我未使用的静态thread_local类成员?


10

我想在我的课程中做一些线程注册,所以我决定为该thread_local功能添加一个检查:

#include <iostream>
#include <thread>

class Foo {
 public:
  Foo() {
    std::cout << "Foo()" << std::endl;
  }
  ~Foo() {
    std::cout << "~Foo()" << std::endl;
  }
};

class Bar {
 public:
  Bar() {
    std::cout << "Bar()" << std::endl;
    //foo;
  }
  ~Bar() {
    std::cout << "~Bar()" << std::endl;
  }
 private:
  static thread_local Foo foo;
};

thread_local Foo Bar::foo;

void worker() {
  {
    std::cout << "enter block" << std::endl;
    Bar bar1;
    Bar bar2;
    std::cout << "exit block" << std::endl;
  }
}

int main() {
  std::thread t1(worker);
  std::thread t2(worker);
  t1.join();
  t2.join();
  std::cout << "thread died" << std::endl;
}

代码很简单。我Bar班有一个静态thread_local成员foo。如果thread_local Foo foo创建了静态,则意味着创建了线程。

但是,当我运行代码时,Foo()打印内容中没有任何内容,并且如果删除了的Bar使用的构造函数中的注释foo,代码也可以正常工作。

我在GCC(7.4.0)和Clang(6.0.0)上进行了尝试,结果是相同的。我猜编译器发现它foo未使用并且没有生成实例。所以

  1. 编译器是否忽略了该static thread_local成员?我该如何调试?
  2. 如果是这样,为什么普通static会员没有这个问题?

Answers:


9

您的观察没有问题。 [basic.stc.static] / 2禁止消除具有静态存储持续时间的变量:

如果具有静态存储持续时间的变量具有初始化或具有副作用的析构函数,则即使它看起来未使用,也不应将其消除,除非可以按照[class.copy]中的说明消除类对象或其复制/移动。 。

其他存储持续时间不存在此限制。实际上,[basic.stc.thread] / 2表示:

具有线程存储持续时间的变量应在其第一次使用odr之前初始化,如果已构造,则应在线程退出时销毁。

这表明除非使用odr,否则无需构造具有线程存储持续时间的变量。


但是为什么会有这种差异呢?

对于静态存储持续时间,每个程序只有一个变量实例。其构造的副作用可能很明显(有点像程序范围的构造函数),因此需要这些副作用。

但是,对于线程本地存储持续时间,存在一个问题:算法可能启动很多线程。对于大多数这些线程,变量是完全不相关的。如果调用外部物理仿真库std::reduce(std::execution::par_unseq, first, last)最终创建了很多foo实例,那将很有趣,对吗?

当然,可以合理地使用未本地使用的线程本地存储持续时间变量(例如,线程跟踪器)的副作用。但是,保证这一点的优点不足以弥补上述缺点,因此只要不使用这些变量,就可以将其删除。(不过,您的编译器可以选择不执行此操作。您还可以使用自己的包装器来解决std::thread此问题。)


1
好吧...如果该标准这样说的话,那将是这样...但是委员会不将副作用视为静态存储期限是不是很奇怪?
ravenisadesk

@reavenisadesk请参阅更新的答案。
LF

1

我在“ 用于线程本地存储的ELF处理 ”中找到了此信息,可以证明@LF的答案

另外,如果不需要,运行时支持应避免创建线程本地存储。例如,一个加载的模块只能由组成该进程的许多线程中的一个使用。为所有线程分配存储将浪费内存和时间。需要一种惰性方法。这不是太多的额外负担,因为处理动态加载的对象的要求已经要求识别尚未分配的存储。这是在允许所有线程再次运行之前停止所有线程并为所有线程分配存储的唯一选择。

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.