设计模式——多线程下的懒汉式单例

"懒汉"模式虽然有优点, 但是每次调用 GetInstance()静态方法时, 必须判断NULL == m_instance, 使程序相对开销增大。多线程中会导致多个实例的产生, 从而导致运行代码不正确以及内存的泄露。

对于多线程的问题,我们可以看下面这个例子:

#include <iostream>
#include <windows.h>
using namespace std;
//懒汉式
class Singleton
{
private:
    Singleton(){
        Sleep(10);
    }
public:
    static Singleton* getInstance(){
        if(sin == NULL)//懒汉式: 1 每次获取实例都要判断 2 多线程会有问题
        {
            sin = new Singleton;
        }
        return sin;
    }
    static void deleteInstance(){
        if(sin != NULL)
        {
            delete sin;
            sin = NULL;
        }
    }
private:
    static Singleton * sin;//未初始化
};
Singleton * Singleton::sin = NULL; 

假设有多线程,第一个线程进入实例化,然后会运行Sleep函数,第二个线程肯定不是等上一个线程Sleep运行完才执行,然而上一个线程没有实例化完成,所以同样会进行实例化,然后运行Sleep,这样的话,等到Sleep运行完,会有多个实例产生。这样单例模式就失去了意义,浪费了开销。

懒汉式遇上多线程,将可能不再是单例。

 

解决方法:Double-Checked Locking(两次检查)

代码如下:

//临界区
static CCriticalSection cs;

static Singleton *Instantialize()
{
    if(pInstance == NULL) //double check
    {
        cs.Lock(); //只有当 pInstance 等于 null 时, 才开始使用加锁机制 二次检查
        if(pInstance == NULL)
        {
            pInstance = new Singleton();
        } 
        cs.Unlock();
    } return pInstance;
} 
static Singleton *pInstance;

解读:这里有一个临界区的概念,若想详细查看,可看操作系统相关。

  1. 若第一个线程进入,第一次判断为空,继续进入,进行加锁,判断为空,继续进入创建实例。
  2. 在第一个线程创建实例过程中,第二个线程也进入到加锁这行代码,犹豫加锁后还没解锁,线程2进入进入阻塞状态等待解锁,同样后面的线程3等也会进入此状态。
  3. 之后,若线程1执行完实例化,解锁,线程2开始执行,但是已经实例化,所以不重新实例化,解锁跳出,之后的线程也是如此。新进入的线程第一次判空便会直接跳出。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值