所以我读了很多关于为什么这个实现不是线程安全的。但我没有找到答案如何使其线程安全和快速?使其线程安全的变体是添加互斥锁(或者在某些情况下只是关键部分就足够了),但它会使这个方法慢得多。那么是否有一个变体可以使这个代码线程安全快速,或者至少现在像在那里添加互斥量一样慢?
static Singleton& getInstance()
{
static Singleton singleton;
return singleton;
}
PS:是的,当我们使用Singleton指针作为类的成员时,我也阅读了很多关于线程安全的Singletom实现,问题是关于Singleton的这个特定实现,没有指针和新的并且使用惰性求值。< / p>
答案 0 :(得分:2)
对于某些编译器,您所拥有的可能已经具有线程安全保证。如果您不关心代码可移植性并且它适合您,那么请对它感到满意。
如果您有可用的提升线程,则可以使用boost::call_once
进行初始化。这是线程安全的,只在首次初始化时付出代价。
当然,在创建访问它的线程之前,您可以通过初始化(即第一次访问它)来使“Meyers”单例创建完全是线程安全的。如果您已经实施了很多这些单身人士,请考虑这样做。
所有这些,甚至boost::call_once
仅适用于对象的创建。但是,如果由多个线程访问,它的访问可能需要单独的同步技术。
(顺便提一下Meyers Effective C ++的第47项,其中提到这个单例表明该标准的后续版本使其成为线程安全的,后来的编译器也符合它,但是它确实警告你并非所有编译器都是兼容的。)< / p>
答案 1 :(得分:0)
好的,所以如果没有互斥锁就无法做到,但你可以快速制作互斥锁。
首先声明一个类来保存互斥锁和一个就绪标志(下面是InitMutex
)。调用GrabMutex()
且ready
为false
时,会抓取实际的互斥锁。调用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的评论:
“快速,线程安全,懒惰 - 挑选任何两个。”