【问题标题】:Singleton threadsafe class单例线程安全类
【发布时间】:2012-07-26 09:58:26
【问题描述】:

我在多线程环境中做的工作很少。所以,我需要知道下面类的 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 为空,我们将创建对象。所以,它需要一个锁定机制,需要同步,对吧?

【问题讨论】:

标签: c++ thread-safety singleton


【解决方案1】:

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

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

如果您在main 开始之前或结束之后进行访问(例如,从另一个静态对象的构造函数或析构函数),则存在未初始化或已被销毁的危险,在那时候。这就是“延迟初始化”(例如您的第二个版本)的动机。

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

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

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

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

一般而言,C++ 中的静态对象是此类死亡陷阱的雷区(无论您是否尝试将它们包裹在某种单例反模式中),最好避免使用。

【讨论】:

    【解决方案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 段:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        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;
          }
        };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多