命名空间作用域静态存储期变量的初始化是否线程安全?
命名空间作用域静态存储期变量初始化的线程安全标准约定
标准规则原文表述
命名空间作用域inline静态变量规则(同样适用于同作用域非inline变量)
「具有静态存储期的非块级inline变量的动态初始化是否排在main函数第一条语句之前执行,或是延迟执行,属于实现定义行为。若初始化被延迟,则其强发生于该变量的任何非初始化odr-use之前。此类延迟动态初始化在哪些线程中、在程序的哪个时间点执行,属于实现定义行为。」
从该条款可以明确,标准没有对这类变量的并发初始化场景做任何同步要求,也不保证初始化仅执行一次,多线程同时触发初始化时可能出现数据竞争。
块级静态变量规则
「具有静态存储期或线程存储期的块级变量的动态初始化,会在执行流首次经过其声明语句时执行;初始化完成后该变量才被视为已完成初始化。[...] 若变量处于初始化过程中时,有并发执行流进入该声明语句,并发执行需等待初始化完成。」
标准提供的明确线程安全保障范围
- 所有静态初始化(零初始化、常量初始化)阶段不存在线程安全问题,该阶段操作必然在所有动态初始化操作之前完成。
- 若命名空间作用域静态变量的动态初始化发生在
main函数启动之前,由于此时程序仅运行启动线程,不存在并发访问,也不会出现初始化竞争。 - 若动态初始化被延迟触发,标准仅保证初始化必然发生在变量的第一次非初始化odr-use之前,无任何额外的线程安全强制约定。
两类规则差异的原因
块级静态变量的初始化触发点是唯一且明确的:首次执行到声明语句,标准可以很容易地对该点的并发访问行为做出强制同步要求,开销可控。而命名空间作用域静态变量的初始化顺序、触发点都没有明确的全局约定,部分场景下触发点可能分散在多个翻译单元中,如果标准强制要求同步,会对大量无多线程访问需求的场景引入不必要的运行时开销,因此标准仅做最低限度的约定。
注意
标准未对命名空间作用域静态变量的延迟动态初始化提供任何线程安全保障,若存在多线程并发访问的可能,需要开发者自行添加同步逻辑保证初始化安全。
内容的提问来源于stack exchange,提问作者stackoverflow_account_23492384
相关产品推荐
相关产品推荐

