块作用域静态对象的析构函数可能会被多次调用?

4
我刚读了this有关当前boost::mutex实现背后的实际原因的文章,并注意到以下短语:
块范围静态变量还存在潜在的“第一次通过”竞争条件问题,这可能导致在流行编译器上多次运行析构函数,这是未定义的行为。编译器通常使用类似于调用atexit的方式来确保按照构造顺序执行销毁操作,而可能导致构造函数运行两次的初始化竞争也可能导致析构函数注册两次。
这是真的吗?我真的应该检查另一个线程是否已经通过原子操作或类似方法进入此对象的析构函数吗?即使在C++11-C++14中也应该这样做吗?因为据我所知,自C++11以来就不再存在“同时可以从几个线程调用具有静态存储期的相同本地对象的构造函数”的问题了--它要求其他线程应等待构造函数完成。我是对的吗?
1个回答

2
看起来这篇文章是在 C++11 之前写的,其中说到:

[...] C++ 标准的下一个版本计划于2009年发布。[...]

在 C++11 之前,情况就是这样,因为线程不是 C++11 内存模型的一部分,所以在这种情况下发生了什么是未指定的。

这在 C++11 中有所改变,C++11 标准草案 的第 6.7声明语句 中说道(重点在于我):

所有具有静态存储期或线程存储期的块作用域变量在进行任何其他初始化之前都会进行零初始化(8.5)。否则,这样的变量将在控制第一次通过其声明时进行初始化;此类变量被认为在其初始化完成时已经初始化。如果初始化通过抛出异常退出,则初始化未完成,因此下一次控制进入声明时将再次尝试初始化。如果在变量正在初始化时同时并发地进入声明,则并发执行必须等待初始化完成。在 C++11 之前,我们必须像处理任何其他关键部分一样处理静态局部变量。我们可以在 C++ scoped static initialization is not thread-safe, on purpose! 的文章中找到一个关于 C++11 之前情况的优秀描述。

竞争条件如何影响C++11之前静态变量的销毁?编译器不负责清理静态对象的任何线程吗? - eerorika
1
如果进行多个实例化,则可能会出现多个析构函数调用。由于标准没有指定多线程内存模型,因此实现可以以任何可能的方式处理此情况。 - Shafik Yaghmour
在没有C++11的情况下,我们应该如何处理它? - FrozenHeart
@FrozenHeart,你必须像在多线程情况下处理任何其他关键部分一样对待它。 - Shafik Yaghmour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接