【问题标题】:Static pointer to object initialization thread-safety指向对象初始化线程安全的静态指针
【发布时间】:2014-07-24 12:16:38
【问题描述】:

在 C++11 中,以下是线程安全的:

void someFunc()
{
    static MyObject object;
}

但是呢

void someFunc()
{
    static MyObject *ptr = new MyObject();
}

这是否是线程安全的?

正如@Nawaz 在 cmets 中提到的那样,MyObject 构造函数可能不是线程安全的,所以让我们将问题分成几部分:

1) 如果 ctor 是线程安全的(它不访问任何共享状态),这是 static MyObject *ptr = new MyObject(); 线程安全的吗?换句话说,static int *ptr = new int(0); 线程安全吗?

2) 如果 ctor 不是线程安全的,但对象仅通过从不同线程调用 someFunc 来创建,并且构造函数从未在其他任何地方使用,那么这将是线程安全的吗?

【问题讨论】:

  • static MyObject object;not 线程安全的,如果默认构造函数不是线程安全的,即如果它在内部访问可变共享资源,比如通过全局变量或单例。但是,static int object = 100; 是 100% 线程安全的。
  • @Nawaz 考虑到您在评论中对我(现已删除)答案的引用,这是否表明这两个初始化实际上都是线程安全的? (有问题的引用是“如果在初始化变量时控制同时进入声明,则并发执行应等待初始化完成。”,来自 C++11 规范§6.7/ 4).
  • @JoachimPileborg:我不这么认为。标准仅保证这一点:如果多个线程尝试同时开始执行构造函数,则只有其中一个会实际执行它,其余的将等待初始化完成。然而,标准并没有对构造函数本身的线程安全提供任何保证。构造函数本身很可能是线程不安全的(例如,如果它访问 modifiable 全局资源)。
  • @Nawaz 换句话说,我们保证实际初始化是线程安全的,但不是多线程导致重新初始化?
  • 只有一个线程会初始化object,但object的构造函数可能不是线程安全的。

标签: c++ c++11 thread-safety static-initialization


【解决方案1】:

是的,它是线程安全的。这遵循适用于第一个示例的相同保证,即函数的并发执行将仅初始化静态变量一次。由于静态指针必须只被初始化一次,并且初始化它的方式被定义为对new的调用,那么new和它所调用的构造函数都将只被调用一次。假设new对象的构造函数没有做任何不安全的事情,那么整个事情都是安全的。

感谢 Matthieu M. 指出一个异常:如果初始化抛出,它将在下一次(未决或将来)调用该函数时再次尝试。但它仍然是线程安全的,因为直到第一次尝试失败后才会开始第二次尝试。

话虽如此,看到这样的代码令人担忧,因为它似乎可能导致内存泄漏,这可能会被 valgrind 等自动化工具标记,因此最好以某种方式避免这种情况。即使是具有静态成员的类也可能会更好,因为这样会更容易使用在程序结束之前调用的特殊方法来清理静态。

【讨论】:

  • 注意:实际上,exactly once 是错误的。它一次一个,但表达式可能会被计算多次(连续)直到它成功 => 在初始化期间发生异常的情况下,已知对象不会被初始化并再次尝试初始化下次控制流通过定义。
  • statics 是否在某种全局锁下执行?他们每个人都有一把自己的锁还是一把锁?
  • 约翰,你的回答正是我想知道的,我会接受的。但它看起来像@MatthieuM。在他的评论中是正确的,我只是在网络上的某个地方读到了这个。也许,您会编辑您的答案,让未来的读者更清楚?
  • @Niall,是的,那里有一把锁,read Using a C++11 Static Initializer section
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-14
  • 2023-03-30
  • 2015-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多