单例线程安全类

时间:2012-07-26 09:58:27

标签: c++ thread-safety singleton

我在多线程环境中做了很少的工作。所以,我需要知道下面的类的getInstance函数是否是线程安全的。这是单身人士课程:

//Singleton class
class S {
  // intentionally avoided pointer
  static S singleObject;

   // Private constructor   
   s ();
   s (S &);
   s& operator= (const s&);
public:
  // return reference of static object
  s& getInstance ()
  {
    return singleObject;
  }

  /* Normally with static pointer instance, getInstnace look like as
   s& getInstace ()
   {
      // trying to avoid multiple copies of singleObject
      lock_mutex ()

       if (singleObject == null)
          singleObjecct = new S();

      unlock_mutex ();

      return *singleObject;
    }
  */
};

S S::singleObject;

在getInstance函数(未注释)中,返回静态对象的引用。它需要线程安全机制吗?

在第二个getInstance(注释)中,如果singleObject为null,我们创建对象。所以,它需要一个锁定机制,需要同步,对吗?

4 个答案:

答案 0 :(得分:2)

在C ++ 11中,您可以将静态实例放在静态函数中:

class S
{
    private:
        S();
        S(S const&);
        S& operator=(S const&);

    public:
        static S& getInstance ()
        {
            static S singleObject;
            return singleObject;
        }
};

根据标准第6.7.4段:

  

使用static的所有块范围变量的零初始化(8.5)   存储持续时间(3.7.1)或线程存储持续时间(3.7.2)是   在任何其他初始化发生之前执行。不变   具有静态存储的块范围实体的初始化(3.6.2)   持续时间(如果适用)在其块首次执行之前执行   进入。允许实现尽早执行   使用static或thread初始化其他块作用域变量   在与实现相同的条件下的存储持续时间   允许使用static或thread静态初始化变量   命名空间范围内的存储持续时间(3.6.2)。否则这样的变量   在控制第一次通过其声明时初始化;   这样的变量在完成后被认为是初始化的   初始化。如果通过抛出异常退出初始化,   初始化未完成,因此下次将再次尝试   时间控制进入声明。 如果控制进入声明   在初始化变量的同时,并发   执行应等待初始化完成。如果   控制在变量为的时候递归地重新输入声明   在被初始化时,行为是不确定的。

答案 1 :(得分:2)

  

在getInstance函数(未注释)中,返回静态对象的引用。它需要线程安全机制吗?

只要您不在main函数的生命周期内访问它,或者在其他线程可能具有非同步访问权限时修改它,那么从任何线程访问都是安全的。

如果您在main开始之前或结束之后(例如从另一个静态对象的构造函数或析构函数)访问,那么它就有可能无法初始化,或者已经初始化那时候已经被摧毁了。这是"懒惰初始化的动机"比如你的第二个版本。

  

在第二个getInstance(注释)中,如果singleObject为null,我们创建对象。所以,它需要一个锁定机制,需要同步,对吗?

是的,这需要一个锁定机制。对于支持C ++ 11(或类似)线程模型的编译器,一种更简单的方法来实现这样的延迟初始化是使用函数静态变量,它将在第一次进入时以线程安全的方式初始化范围:

S& getInstance ()
{
    static S singleObject;
    return singleObject;
}

这也可以避免你的版本的内存泄漏,但会带来在其他静态对象之前可能被破坏的危险;因此,从静态对象的析构函数访问是不安全的。

一般来说,C ++中的静态对象是这种死亡陷阱的雷区(无论你是否尝试将它们包装成某种单一的反模式),最好避免使用。

答案 2 :(得分:2)

除非您将getInstance声明为静态,否则您将无法调用它。这个错误几乎传播到所有回复。除此之外,我无法为所有答案添加任何更好的内容。

答案 3 :(得分:0)

IIRC这不仅仅是因为这个原因。在调用getInstance之前,它不会初始化(线程安全)。

-edit-我现在记得一些原因。除非调用该方法,否则无法访问。你可以在其他类构造函数中调用它,如果S已初始化或没有,则不必担心。因为在其他类中可以首先构造,在这种情况下会发生崩溃或未定义的行为。

//Singleton class
class S {
  // intentionally avoided pointer

   // Private constructor   
   s ();
   s (S &);
   s& operator= (const s&);
public:
  // return reference of static object
  s& getInstance ()
  {
    static S singleObject;
    return singleObject;
  }
};