群里热烈的讨论了线程安全的单例;
一路从volitile扯到汇编,从代码到论文,真是够了;
首先volitile 跟多线程没直接的关系,或许有些特定情况下会用到;至于spincout之类的实现,内部使用了volitile,这类就不多说了,有兴趣的可以自己看实现;
千万别使用下面代码 ,
不要使用双检测,不要使用双检测,不要使用双检测:
static Singleton* Singleton::instance()
{
if (!_instance)
{
lock();
if (!_instance)
{
//关键在这部, 会拆成3个步骤来执行;
/*
1. 分配内存 -> 没问题
2. 调用构造函数
3. 把地址赋值给 _instance
*. 核心在 2,3 可能会乱序 ,即 1-2-3 , 1-3-2
一旦 1-3-2 发生,直接完蛋
*/
_instance = new Singleton;
}
unlock();
}
return _instance;
}
上面代码注释中提到 1-3-2 这种情况一旦发生, 如另一线程进入,直接可得到 _instance ,而这个_instance 目前不确定有没有
调用构造函数,灾难由此来了;
如果非要使用上面那个版本,怎么办 ?
static Singleton* Singleton::instance()
{
if (!_instance)
{
lock();
if (!_instance)
{
// 3行代码替换 一个 new Singleton
// 完全按照 分配内存 - 调用构造 - 赋值地址
Singleton * p = static_cast<Singleton*>(operator new(sizeof(Singleton)));
new (p)SingleTon();
_instance = p;
}
unlock();
}
return _instance;
}
问题是为什么要搞那么复杂 , 最简单又安全的版本:
c11 及以后的版本:
c11标准定了 , 当一个线程正在初始化一个变量的时候,其他线程必须得等到该初始化完成以后才能访问它;
注意:这个版本在c11以前不是安全的.
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
c11以前的版本:
简单安全又通用
class Singleton
{
private:
static Singleton _instance;
public:
static Singleton& getInstance() {
return _instance;
}
}
Singleton Singleton::_instance;