|
|
Double Checked Locking模式:
class Singleton
{
public:
static Singleton *instance (void)
{
if (instance_ == 0)
{
Guard lock (lock_)
if (instance_ == 0)
{
instance_ = new Singleton;
}
}
return instance_;
}
private:
static Singleton *instance_;
};
上面所说的Double Checked Locking模式有下面优点:
1、最小化加锁。通过实现两个flag检测(第一个flag采用模糊匹配,第二个flag采用精确定位),Double Checked Locking模式实现通常用例的优化。
但是同时此模式存在也会带来致命的缺陷:
1、如果使用Double Checked Locking模式的软件被移植到没有原子性的指针和正数赋值语义的硬件平台上。例如,如果一个instance_指针被用来作为Singleton实现的flag,instance_指针中的所有位(bit)必须在一次操作中完成读和写。如果将new的结果写入内存不是一个原子操作,其他的线程可能会试图读取一个不健全的指针,这将导致非法的内存访问。在一些允许内存地址跨越对齐边界的系统上这种现象是可能的,因此每次访问需要从内存中取两次。在这种情况下,系统可能使用分离的字对齐合成flag,来表示 instance_指针。
2、某些编译器可能在优化过程中采用某种缓冲手段来优化flag,或是移除了第二个flag==0检测。
针对这两个缺陷,现在我们提出了一种解决方案:
Double Checked Locking 模式:
01 class Singleton
02 {
03 public:
04 static Singleton *instance (void)
05 {
06 // 第一个模糊检测
07 if (instance_ == 0)
08 {
09 //模糊区域
10 Guard guard (lock_);
11 // 第二次检测
12 if (instance_ == 0) //精确定位
13 instance_ = new Singleton;
14 }
15 return instance_;
16 // 释放锁.
17 }
18 private:
19 static Mutex lock_;
20 static Singleton *instance_;
21 };
改进版本的Double Checked Locking 模式:
01 class Singleton
02 {
03 public:
04 static Singleton *instance (void)
05 {
06 // 第一个模糊检测
07 if (instance_test_ == 0)//<=if (instance_ == 0)
08 {
09 //模糊区域
10 Guard guard (lock_);
11 // 第二次检测
12 if (instance_ == 0)//精确定位
A1 {
13 instance_ = new Singleton;
A2 instance_test_ = instance;
A3 }
14 }
15 return instance_;
16 // 释放锁.
17 }
18 private:
19 static Mutex lock_;
20 static Singleton *instance_;
A4 static Singleton *instance_test_;
21 };
分析:
如果 instance_test_ == 0 则有可能instance_没有实例化、也有可能没有完全实例化、也有可能没有将new 的结果完全写入 instance,也有可能已经将new 的结果写入instance,但无论怎么样进入08--14代码都是没有问题的
如果instance_test_ != 0 即使flag_存在挥发性、写操作的非原子性,至少这个标志不为零时已经说明先前已经有代码走过第13行了,当然对象就已经生成并将对象结果写入instance_了。
很容易验证改进版本的Double Checked Locking 模式解决了前一模式带来的缺陷:
(即使此时 instance_test_存在非原子性操作,可写操作的非原子性我们依然能够证明)
这里引入一个instance_test 的目的就是采用这个变量检测,这段代码是否已经走过某一段路经的原理,这里我把它叫做检测路经法吧(Checking Road)
|
|