静态指针对象初始化的线程安全性

9

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

void someFunc()
{
    static MyObject object;
}

但是这和什么有关呢?

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

那么这个代码是线程安全的吗?

正如@Nawaz在评论中提到的,可能MyObject构造函数不是线程安全的,因此让我们分成几个部分来回答问题:

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

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


4
如果默认构造函数不是线程安全的,即如果它通过全局变量或单例模式等内部访问可变共享资源,则static MyObject object;不是线程安全的。然而,static int object = 100;是100%线程安全的。 - Nawaz
1
@Nawaz,考虑到您在评论中给出的引用(现已删除),这不会表明两个初始化实际上都是线程安全的吗?(有关问题的引用是“如果控制同时进入声明并且正在初始化变量,则并发执行应等待初始化完成”,来自C++11规范§6.7 / 4)。 - Some programmer dude
1
@JoachimPileborg:我不这么认为。标准只保证:如果多个线程尝试同时启动执行构造函数,则只有一个线程实际上会执行它,其余线程将等待初始化完成。然而,标准并不保证构造函数本身的线程安全性。如果构造函数访问可修改的全局资源,那么它本身可能是线程不安全的。 - Nawaz
@Nawaz 我想我们是在用不同的词汇表达相同的意思。 :) - Some programmer dude
@Nawaz,感谢您指出构造函数的问题。我现在已经编辑了问题。 - Vladimir
显示剩余3条评论
1个回答

9
是的,它是线程安全的。这与第一个例子适用的同样的保证有关,即函数的并发执行将恰好一次初始化静态变量。由于静态指针必须恰好初始化一次,并且初始化的方法被定义为调用new,因此new和它调用的构造函数都将被恰好调用一次。假设new对象的构造函数不执行任何不安全操作,整个过程将是安全的。
感谢Matthieu M.指出一个例外:如果初始化引发异常,则下一个(待处理或未来的)函数调用将再次尝试初始化。虽然如此,但仍然是线程安全的,因为第二次尝试将在第一次失败后才开始。
话虽如此,看到这样的代码令人担忧,因为它似乎很可能会导致内存泄漏,这可能会被像valgrind这样的自动化工具标记出来,因此最好通过某种方式避免这种情况。即使是带有静态成员的类也可能更好,因为这样就可以使用特殊方法清理静态内容,在程序结束之前调用。

3
注意:实际上,“仅一次”是不正确的。它应该是“一个接一个”,但这个表达式可能会被多次(连续地)评估,直到成功为止 => 如果在初始化过程中发生异常,则已知该对象尚未初始化,并且在下一次控制流通过定义时将再次尝试初始化。 - Matthieu M.
这些 static 是在某种全局锁下执行的吗?它们会有自己的锁还是一个单一的锁? - Niall
John,你的回答正是我想知道的,我会接受它。但是@MatthieuM.在他的评论中是正确的,我刚刚在网上读到了这个。也许,你可以编辑你的答案,为未来的读者澄清一些事情? - Vladimir
1
@Niall,是的,那里有一个锁,请阅读“使用C++11静态初始化器”部分 - Vladimir

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