这种创建静态实例线程的方法是否安全?

时间:2010-12-18 06:58:58

标签: c++ thread-safety static-members

我有以下示例C ++代码:

class Factory
{
public:
    static Factory& createInstance()
    {
        static Factory fac;
        return fac;
    }

private:
    Factory() 
    {
        //Does something non-trivial
    }
};

我们假设createInstance同时被两个线程调用。那么生成的对象是否会正确创建?当第一个线程在createInstance的构造函数中时第二个线程进入Factory调用会发生什么?

4 个答案:

答案 0 :(得分:6)

C ++ 11及更高版本:本地静态创建是线程安全的。

该标准保证:

  • 创建已同步。
  • 如果创建抛出异常,则下次执行流程通过变量定义点时,将再次尝试创建。

通常采用双重检查实现:

  • 首先检查线程局部标志,如果设置,则访问该变量。
  • 如果尚未设置,则采用更昂贵的同步路径,如果之后创建变量,则设置线程本地标志。

C ++ 03和C ++ 98:标准没有线程。

就标准而言,没有线程,因此标准中没有关于跨线程同步的规定。

然而,有些编译器实现了 more 而不是标准的任务,无论是以扩展的形式还是通过提供更强的保证,所以请查看您感兴趣的编译器。如果它们是高质量的编译器,很有可能他们会保证。

最后,它可能没有必要是线程安全的。如果在创建任何线程之前调用此方法,那么您可以确保在真正的多线程发挥作用之前将其正确初始化,并且您可以巧妙地解决问题。

答案 1 :(得分:2)

查看this page,我会说这不是线程安全的,因为构造函数可能在最终分配变量之前被多次调用。可能需要InterlockedCompareExchange(),您可以在其中创建变量的本地副本,然后通过互锁函数以原子方式将指针指定给静态字段,如果静态变量为null。

答案 2 :(得分:0)

当然它是线程安全的!除非你是一个完整的疯子并且从静态对象的构造函数中生成线程,否则在调用main()之后你将没有任何线程,并且createInstance方法只是返回对已经构造的对象的引用,这是不可能的失败。 ISO C ++保证在调用main()之后第一次使用之前构造对象:不能保证在调用main之前,但必须在第一次使用之前,因此所有系统都将在执行之前执行初始化调用main()。当然,ISO C ++没有在线程或动态加载的情况下定义行为,但是主机级机器的所有编译器都提供了这种支持,并且会尽可能保留为单线程静态链接代码指定的语义。

答案 3 :(得分:0)

实例化(第一次调用)本身是threadsafe

但是,一般来说,后续访问不会。例如,假设在实例化之后,一个线程调用一个可变的Factory方法,而另一个线程在Factory中调用一些访问器方法,那么你将遇到麻烦。

例如,如果您的工厂保留了创建的实例数量的计数,那么在没有围绕该变量的某种互斥体的情况下,您将遇到麻烦。

但是,如果Factory真的是一个没有状态的类(没有成员变量),那么你就可以了。