使用惰性评估使Meyers的Singleton线程安全快速

时间:2012-04-03 13:38:26

标签: c++ multithreading design-patterns thread-safety singleton

所以我读了很多关于为什么这个实现不是线程安全的。但我没有找到答案如何使其线程安全和快速?使其线程安全的变体是添加互斥锁(或者在某些情况下只是关键部分就足够了),但它会使这个方法慢得多。那么是否有一个变体可以使这个代码线程安全快速,或者至少现在像在那里添加互斥量一样慢?

static Singleton& getInstance()
{
     static Singleton singleton;
     return singleton;
}

PS:是的,当我们使用Singleton指针作为类的成员时,我也阅读了很多关于线程安全的Singletom实现,问题是关于Singleton的这个特定实现,没有指针和新的并且使用惰性求值。< / p>

3 个答案:

答案 0 :(得分:2)

对于某些编译器,您所拥有的可能已经具有线程安全保证。如果您不关心代码可移植性并且它适合您,那么请对它感到满意。

如果您有可用的提升线程,则可以使用boost::call_once进行初始化。这是线程安全的,只在首次初始化时付出代价。

当然,在创建访问它的线程之前,您可以通过初始化(即第一次访问它)来使“Meyers”单例创建完全是线程安全的。如果您已经实施了很多这些单身人士,请考虑这样做。

所有这些,甚至boost::call_once仅适用于对象的创建。但是,如果由多个线程访问,它的访问可能需要单独的同步技术。

(顺便提一下Meyers Effective C ++的第47项,其中提到这个单例表明该标准的后续版本使其成为线程安全的,后来的编译器也符合它,但是它确实警告你并非所有编译器都是兼容的。)< / p>

答案 1 :(得分:0)

好的,所以如果没有互斥锁就无法做到,但你可以快速制作互斥锁。

首先声明一个类来保存互斥锁和一个就绪标志(下面是InitMutex)。调用GrabMutex()readyfalse时,会抓取实际的互斥锁。调用ReleaseMutex()时,它会根据GrabMutex()发送的标记执行正确的操作。在第一次通过之后,ready会在静态对象现在初始化时生效,因此不需要抓取互斥锁。

现在,声明一个在构造函数中调用GrabMutex()并在析构函数中调用ReleaseMutex(flag)的类,在本地保存标记(下面是InitMutexHolder)。

现在,在常规getSingleton函数中实例化该类。这将确保单例初始化在第一次通过时进行互斥,如果多个线程争用它们将在互斥锁上排队。但是一旦初始化单例,ready成立,访问速度就会很快。执行return theSingleton后会彻底调用析构函数,这会释放互斥锁(如果未使用互斥锁,则不执行任何操作)。

但是,theSingleton()函数中的主体代码没有改变,我们只在每个单例中添加了一个控件对象,并在调用中添加了一个管理线程安全的堆栈对象。

关于写入障碍的注意事项:由于代码在ready为假时是安全的,并且ready在对象初始化之前无法成立,因此代码是完全安全的,假设写入立即可见。但是,ready=true可能会有延迟,因为在设置就绪后没有写屏障。但是,在此期间,安全性仍然存在,因为ready=false是保守的,安全的案例。

class InitMutex
{
public:
   InitMutex() : ready(false) { }

   bool  GrabMutex()
   {
      if (!ready)
      {
         mutex.Grab();
         return true;
      }
      else
      {
         return false;
      }
   }

   void ReleaseMutex(bool flagFromGrabMutex)
   {
      if (flagFromGrabMutex)
      {
          mutex.Release();
          ready = true;
      }
   }

   Mutex   mutex;
   bool    ready;
};

class InitMutexHolder
{
public:
    InitMutexHolder(InitMutex & m)
    : initMutex(m)
    {
       inMutex = initMutex.GrabMutex();
    }
    ~InitMutexHolder()
    {
       initMutex.ReleaseMutex(inMutex);
    }

private:
    bool inMutex;
    InitMutex & initMutex;
};

static InitMutex singletonMutex;
static Singleton & getSingleton()
{
    InitMutexHolder mutexHolder(singletonMutex);
    {
       static Singleton theSingleton;
       return theSingleton;
    }   
}

答案 2 :(得分:-1)

所以似乎我的问题的答案最好地表达了Fred Larson的评论:

“快速,线程安全,懒惰 - 挑选任何两个。”