C++ 如何以线程安全的方式避免静态数据成员初始化失败?

C++ 如何以线程安全的方式避免静态数据成员初始化失败?,c++,concurrency,thread-safety,C++,Concurrency,Thread Safety,考虑这一点: class A { public: A(const char* s) { /*...*/ }; static const A& get_default() { /* avoid static data memember init fiasco */ static const A& a = A("default"); /* ..but not thread-safe */ return a; } }; 如何使g

考虑这一点:

class A {
public:
    A(const char* s) { /*...*/ };
    static const A& get_default() { /* avoid static data memember init fiasco */
        static const A& a = A("default"); /* ..but not thread-safe */
        return a;
    }
};

如何使
get\u default()
线程安全?或者我如何找到一种方法来获得
a::a
,避免数据成员init失败和并发。我不喜欢使用任何锁。

以下内容将利用这样一个事实:调用
main()
之前的初始化将在单个线程上进行,并且
pInstance
指针必须在静态对象的动态初始化之前进行零初始化(3.6.2“非本地对象的初始化”)

因此,第一次调用
get_default()
时,
pInstance
将为空-无论该调用是否是由于
A::pInstance
的初始化引起的。这将导致
get_default()。需要明确的是,
pInstance
的初始值设定项将强制在init线程上调用
get_default()
,即使没有其他操作

get\u default()
的后续调用不会修改
pInstance
,因此它是线程安全的

请注意,更简单的是:

class A {
public:
    A(const char* s) { /*...*/ };

    static const A& get_default() { 
        static const A& a = A("default"); /* ..but not thread-safe */
        return a;
    }
};

namespace {
    A const& trigger = A::get_default();
}
出于同样的原因,它也应该是线程安全的,但它有几个方面让我不太确定它是否会有一些潜在的缺点:

  • 如果工具链确定
    trigger
    没有在其他地方使用(即使我们将其从匿名名称空间中删除,这也适用),我担心它可能会从程序映像中删除。我不确定该标准在防止这种优化方面会做出什么承诺——特别是如果
    class A
    住在图书馆里
  • 大概编译器只是检查一个隐藏的静态标志,以确定
    get_default()
    中的本地
    static
    变量的初始化之前是否已经运行过。然而,对于使用什么机制,实际上并没有什么承诺(我还没有研究C++0x对此可能会说些什么)。在第一个示例中,init time
    get\u default()
    调用之后的线程安全检查就在那里-保证

以下内容将利用以下事实:调用
main()
之前的初始化将在单个线程上进行,并且
pInstance
指针必须在静态对象的动态初始化之前进行零初始化(3.6.2“非本地对象的初始化”)

因此,第一次调用
get_default()
时,
pInstance
将为空-无论该调用是否是由于
A::pInstance
的初始化引起的。这将导致
get_default()。需要明确的是,
pInstance
的初始值设定项将强制在init线程上调用
get_default()
,即使没有其他操作

get\u default()
的后续调用不会修改
pInstance
,因此它是线程安全的

请注意,更简单的是:

class A {
public:
    A(const char* s) { /*...*/ };

    static const A& get_default() { 
        static const A& a = A("default"); /* ..but not thread-safe */
        return a;
    }
};

namespace {
    A const& trigger = A::get_default();
}
出于同样的原因,它也应该是线程安全的,但它有几个方面让我不太确定它是否会有一些潜在的缺点:

  • 如果工具链确定
    trigger
    没有在其他地方使用(即使我们将其从匿名名称空间中删除,这也适用),我担心它可能会从程序映像中删除。我不确定该标准在防止这种优化方面会做出什么承诺——特别是如果
    class A
    住在图书馆里
  • 大概编译器只是检查一个隐藏的静态标志,以确定
    get_default()
    中的本地
    static
    变量的初始化之前是否已经运行过。然而,对于使用什么机制,实际上并没有什么承诺(我还没有研究C++0x对此可能会说些什么)。在第一个示例中,init time
    get\u default()
    调用之后的线程安全检查就在那里-保证

FWIW,这在C++0x中已经是完全线程安全的。在C++0x中可能是这样的(您可以发布一个链接)。但是c++0x还不是标准的。这不是静态初始化顺序的失败@太空:的确。但是OP的代码片段是避免它的一种方法。但它在多线程上下文中有问题。请参阅:FWIW,这在C++0x中已经是完全线程安全的。在C++0x中可能是这样(您可以发布一个链接)。但是c++0x还不是标准的。这不是静态初始化顺序的失败@太空:的确。但是OP的代码片段是避免它的一种方法。但它在多线程上下文中有问题。请参阅:谢谢Michael。这就是我一直在寻找的解决方案!谢谢你,迈克尔。这就是我一直在寻找的解决方案!