thread_local数据是由C ++运行时使用“.tdata / .tbss”部分中的数据初始化的值(假设为ELF abi)。因此,添加另一对名为“.tinit”/“。tfini”的部分似乎是可行的。这些部分可能包含在启动和终止从属线程时以及在线程本地存储器建立之后总是运行的代码(类似于在主线程开始/结束时执行的“.init”/“。fini”部分)。然后,链接器可以将非平凡线程局部对象的构造函数和析构函数集中到那些构造函数和析构函数中。
假定的“.tfini”部分的功能目前由“__cxa_thread_atexit”和朋友的动态机制处理(具有相当大的开销)。但是,运行时不提供“自动”线程初始化代码的专用检测:对于所有非平凡线程本地对象,编译器必须发出在每次访问时检查的保护变量。
所以问题是:上面概述的方法是由线程本地数据功能架构师评估的吗?确定了哪些缺点(除了线程执行语义的变化,这些变化显然不是一成不变的)?
答案 0 :(得分:0)
正如您所指出的,ELF线程局部变量(即标有__thread
)必须使用常量表达式进行初始化。这是由于一些关于实施的设计决定。
Insight 1 :大多数线程都不会访问应用程序中可用的所有线程本地数据。想象一下,一些计算库启动了几个线程来加速执行 - 这些线程最有可能访问这个库的内部线程本地,而不是其他库中的线程本地。这就是为什么Glibc按需分配线程本地存储 即app时。代码首先通过__tls_get_addr
访问它(一些 TLS块将在启动时分配,但这更多是优化而不是要求)。
洞察力2 :允许任意构造函数在TLS分配期间工作(即在__tls_get_addr
内),这将是巨大的死锁种族来源{{1}可以在代码中的任意点调用(每当第一次引用TLS变量时)。
我相信这是导致设计人员完全禁止动态初始化TLS变量的原因。