动态加载库中的 thread_local 静态变量——它们是什么时候创建的?

问题描述

cppreferencethread_local 变量上声明以下内容

对象的存储在线程开始时分配,在线程结束时释放。每个线程都有自己的对象实例。只有声明为 thread_local 的对象具有此存储持续时间。

我考虑使用在共享库中声明的 thread_local static 成员变量,该变量在运行时通过 dlopen / LoadLibrary 加载。由于完全有可能在加载这个库的时间点已经有相当多的线程在运行,并且其中一些线程稍后将访问该变量,我想知道如果在线程开始时分配存储,这是如何工作的?如果在创建线程的时间点程序中不存在该变量,则这显然无法按照那里的说明工作。此外,如果一个进程正在运行,这似乎是一种资源浪费。如果只有少数线程实际访问该变量,则 100 个线程将为每个线程创建该线程局部变量的实例。

那么,这里的文档是否不正确,或者我在这里尝试的内容是否有可能导致未定义的行为?如果文档完全不正确,我在哪里可以找到对现实中可以预期的可靠描述?如果它是实现定义的,我特别感兴趣的是 clang 如何在 macOS 和 Windows 上处理它。

解决方法

cppreference 所说的是意译。 the standard 中的实际内容是

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

那里没有关于存储何时被分配的信息,只是它在线程的持续时间内持续。这意味着它可以在创建线程时分配,或者在第一次使用变量时分配,或者可能是两者的组合。

在分配存储时,可能不会构造变量(我假设这就是您说“创建实例”时的意思)。这取决于变量的定义位置和方式。但是,如果它被构造,它不会被销毁,直到线程结束。

支持通过 dlopenLoadLibrary 动态加载库是编译器/平台扩展,而不是语言的一部分。它与 thread_local 的交互方式也将取决于平台。