2

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

4

4 回答 4

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 段:

具有静态存储持续时间 (3.7.1) 或线程存储持续时间 (3.7.2) 的所有块范围变量的零初始化 (8.5) 在任何其他初始化发生之前执行。如果适用,具有静态存储持续时间的块范围实体的常量初始化(3.6.2)在其块首次进入之前执行。允许实现在与允许实现在命名空间范围(3.6.2)中静态初始化具有静态或线程存储持续时间的变量相同的条件下,对具有静态或线程存储持续时间的其他块范围变量执行早期初始化。否则,此类变量在控件第一次通过其声明时被初始化;这样的变量在其初始化完成时被认为已初始化。如果在变量初始化时控制同时进入声明,则并发执行将等待初始化完成。如果在初始化变量时控件以递归方式重新进入声明,则行为未定义。

于 2012-07-26T10:44:14.897 回答
2

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

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

如果您在main开始之前或结束之后进行访问(例如,从另一个静态对象的构造函数或析构函数),那么此时它可能没有被初始化,或者已经被销毁。这就是“延迟初始化”的动机,例如您的第二个版本。

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

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

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

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

通常,C++ 中的静态对象是此类死亡陷阱的雷区(无论您是否尝试将它们包装在某种单例反模式中)并且最好避免。

于 2012-07-26T11:15:42.053 回答
2

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

于 2012-07-26T11:59:27.200 回答
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;
  }
};
于 2012-07-26T10:27:19.960 回答