线程局部变量的初始化

Spi*_*ros 3 c++ multithreading thread-local-storage c++11

我最近使用了一个对象,其目的是将内存作为单例进行分配和释放。就像是

class MyValue
{
  // ...

  static Allocator& GetAllocator()
  {
    static Allocator allocator;
    return allocator;
  }

  // ...
};
Run Code Online (Sandbox Code Playgroud)

后来我意识到它Allocator不是线程安全的:当多个线程同时使用同一个分配器时,偶尔会发生奇怪的事情,导致断言和分段错误。

解决方案:为不同的线程使用不同的分配器:

class MyValue
{
  // ...

  static Allocator& GetAllocator()
  {
    thread_local static Allocator allocator;
    return allocator;
  }

  // ...
};
Run Code Online (Sandbox Code Playgroud)

惊人的!我的问题都消失了!只有一个问题: 每次创建线程时我的分配器变量都会被初始化,即使大多数线程不会使用该变量?

分配器的初始化可能是繁重的操作,因此我希望仅在实际需要时才对其进行初始化,而不是在每个线程中进行初始化。

我读到thread_local变量是由每个线程分配的。这是否意味着它们也是被构建的?这种分配(或构造)是针对创建的每个线程系统地发生还是仅针对使用它的线程发生?

我依稀记得在课程中听说过有关线程和线程本地存储的大多数细节都依赖于平台。如果是这样的话,我对 Linux 和 FreeBSD 特别感兴趣。


相关(有趣的读物,但我在那里找不到答案):

Nat*_*ica 7

[basic.stc.thread] 状态

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

  2. 具有线程存储持续时间的变量应在首次 odr 使用(6.2)之前初始化,并且如果构造,应在线程退出时销毁。

因此,您将在每个线程中获得该对象的存储空间。我们还有 [stmt.dcl]/4 指出

具有静态存储持续时间(6.7.1)或线程存储持续时间(6.7.2)的块作用域变量的动态初始化在控制第一次通过其声明时执行;这样的变量在初始化完成后就被认为已初始化。

因此,如果我们到达声明,那么该对象将被初始化,并且如果它有构造函数,它将被调用。

如果我们将所有这些放在一起,您将拥有等于实际到达的线程数的构造函数和析构函数调用数量

thread_local static Allocator allocator;
Run Code Online (Sandbox Code Playgroud)