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

问题描述 投票:6回答:4

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

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

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

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

c++ thread-safety static-members
4个回答
7
投票

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

该标准保证:

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

它通常用双重检查实现:

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

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

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

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

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


2
投票

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


0
投票

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


0
投票

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

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

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

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

热门问题
推荐问题
最新问题