懒汉式实现的单例模式
上文中给出的懒汉式实现的单例模式,在单线程中使用时完全没有问题的,但是在多线程中使用会存在多次对象多次创建的问题。
- 线程A进入
getInstance
方法,并进入到if判断,因为现在m_psl是空指针,这时刚好线程A时间片用完,开始切换到线程B,线程B也调用getInstance
方法创建了一个对象 - 线程B时间片用完,切换到线程A,这时线程A从if判断成功之后的下不开始,又创建了一遍对象,这时已经两次创建对象了
为了解决这个问题需要使用双检测,详情可参考
C++ and the Perils of Double-Checked Locking
里面详细说明了双重检测的方法,和最终的解决方案。
要想解决上述懒汉单例模式中的问题,需要引入这个双重检测的功能,实现也只需将图中的代码在加锁的基础上,在添加一层检测
临界区(Critical Section)。临界区对象通过提供一个进程内所有线程必须
共享的对象来控制线程。只有拥有那个对象的线程可以访问保护资源。在另一个线
程可以访问该资源之前,前一个线程必须释放临界区对象,以便新的线程可以索取对象的访问权
最终实现效果如下
if (m_psl == NULL)
{
// 这里线程开始资源竞争,只有一个线程能获取lock的资源
lock();
if (m_psl == NULL)
{
m_psl = new Singelton;
}
unlock();
}
说明:
lock
里面判断一次,因为可能有多个线程在lock
处等待,一个成功之后,会将m_psl设置为非空,这样下个线程就算拿到lock资源,再进去发现指针非空就离开了
lock
外判断一次,是因为获取锁,是很浪费时间的,获取锁之外还有一层判断,那么在第二次获取单例对象的时候,lock
外的if判断发现指针已经非空,就不会再获取锁了,直接返回了对应的对象,这样双层检测,即保证了对象创建的唯一性,又减少了获取锁浪费的时间和资源
不安全的单例模式代码:
#include <iostream>
using namespace std;
//懒汉式
class Singelton
{
private:
Singelton()
{
cout << "Singelton 构造函数执行" << endl;
}
public:
static Singelton *getInstance()
{
if (m_psl == NULL)
{
m_psl = new Singelton;
}
return m_psl;
}
static void FreeInstance()
{
if (m_psl != NULL)
{
delete m_psl;
m_psl = NULL;
}
}
private:
static Singelton *m_psl;
};
Singelton *Singelton::m_psl = NULL;
void main041()
{
Singelton *p1 = Singelton::getInstance();
Singelton *p2 = Singelton::getInstance();
if (p1 == p2)
{
cout << "是同一个对象" << endl;
}
else
{
cout << "不是同一个对象" << endl;
}
Singelton::FreeInstance();
return ;
}