在类似于Scott Meyer的单例习惯用法的实现中使用新线程安全来实例化单例吗?

时间:2018-01-22 20:29:29

标签: c++ c++11

我最近来了this question,并对Instance()函数实现产生怀疑:

class Configuration
{
public:
    static Configuration* Instance() {
        static Configuration * myInstance = new Configuration();
        return myInstance;
    }

    int i;
    // delete copy and move constructors and assign operators
    Configuration(Configuration const&) = delete;             // Copy  construct
    Configuration(Configuration&&) = delete;                  // Move construct
    Configuration& operator=(Configuration const&) = delete;  // Copy assign
    Configuration& operator=(Configuration &&) = delete;      // Move assign

protected:
    Configuration() {

    }
    ~Configuration() {}

    // ...
}

不幸的是,OP似乎无法提供再现他们声称的读取访问冲突的MCVE。

  • 在该实现中使用实例指针和new仍然保证线程安全(竞争条件可能是导致该错误的潜在原因)?

以下是working code的示例,但只涉及一个线程。

2 个答案:

答案 0 :(得分:1)

  

在该实现中仍然使用实例指针和new   保证线程安全(竞争条件可能是潜在的   这个错误的原因)?

是的,它是线程安全的。

来自N4659:

  

9.7声明声明[stmt.dcl]

     

使用静态存储动态初始化块范围变量   持续时间(6.7.1)或线程存储持续时间(6.7.2)执行   第一次控制通过其声明;这样的变量是   在初始化完成时考虑初始化。如果   初始化通过抛出异常退出   是不完整的,所以下次控制时会再次尝试   进入宣言。 如果控件同时进入声明   在初始化变量时,并发执行   应等待初始化完成。如果控制   在变量存在时递归地重新输入声明   初始化后,行为未定义。

由于myInstance是一个块范围变量,其静态存储持续时间是动态初始化的,因此即使涉及多个线程,代码也是线程安全的。

答案 1 :(得分:0)

就像FYI一样,想要添加以下参考资料,该参考资料解释何时使用指针,何时不使用: https://isocpp.org/wiki/faq/ctors#construct-on-first-use-v2

基本上使用指针不会泄漏,因为OS会在进程存在后回收内存,这在大多数情况下可能是最好的方法。但是,如果单例需要在析构函数中关闭另一个资源,即文件,那么这就是一个问题。在这种情况下,不要将静态局部变量用作指针,而只使用静态局部对象。确保在析构函数中使用此对象的任何其他静态对象也在其构造函数中使用,以确保程序存在时,以正确的顺序调用它们的析构函数。