现在的位置: 首页 > 综合 > 正文

关于Singleton模式中的Double Check机制

2012年02月13日 ⁄ 综合 ⁄ 共 1274字 ⁄ 字号 评论关闭

在多线程环境下,使用Singleton模式很重要的一点就是要保证用Double Check机制保证线程安全。

很多时候, 我们通常需要使用singleton模式来保证对象实例的唯一性。通常我们是这么写的:

class Singleton
{
private:
    static Singleton *instance;
public:
    static Singleton* getInstance();
private:
    Singleton();//将构造函数设为Private以保证只能通过getInstance获取对象实例.
};
Singleton *Singleton::instance = NULL;
//版本一:
Singleton* Singleton::getInstance()
{

    if(NULL == instance) //检查是否已经生成对象了
    {

    //对象构造区域
        instance = new Singleton();
    }
    return instance;
}
Singleton::Singleton()
{
//initializing...
}

然而,如果在多线程环境下,Singleton::getInstance() 同时被多个线程调用,也许第一个线程在通过if(NULL == instance)语句后被中断挂起,这时其它线程也会进入该区域,这时instance = new Singleton();语句就会被调用两次或者更多,违背了singleton模式的初衷。为了保证对象构造区域为一个互斥区间,这时我们考虑引入mutex互斥信号变量。比如:

//版本2:
Singleton* Singleton::getInstance()
{
    lock(mutex);

    if(NULL == instance) ////检查是否已经生成对象了
    {
        //对象构造区域

        instance = new Singleton();
    }
    release(mutex);
    return instance;
}

现在看起来已经足够安全了,只可能同时有一个线程进入对象构造区域。

此时出现了一个性能问题,每次调用getInstance方法时都需要执行lock(mutex)与release(mutex)的操作,而事实上第一次调用之后,instance就不是NULL值了。

这时候我们就设计一个Double Check的机制:

//版本三:
Singleton* Singleton::getInstance()
{
    if(NULL == instance)
    {
    //对象实例第一次被创建后, 没有线程会进入该区域了, 因此该版本的性能与版本一几乎相同,且安全性与版本二一样好
    lock(mutex);
    if(NULL == instance) //检查对象是否已经存在
    {
        //对象构造区域
        instance = new Singleton();
    }
    release(mutex);
    }
    return instance;
}

抱歉!评论已关闭.