现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

线程安全的单例模式

2019-05-17 05:29 工业·编程 ⁄ 共 1619字 ⁄ 字号 暂无评论

1.含义

一个类只能实例化出一个对象

2.单例模式实现的两种方式

饿汉模式

我们在程序运行之初就将对象创建好,就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。是以空间换时间的做法。这样程序运行中想要调用这个实例时都可以直接使用。举个例子:就像是一个人在吃完饭后立刻就去将碗洗了,这样以便于在下一次开饭的时候可以马上吃到饭。

实现原理:

为了在运行前就实例化好对象,并且不允许其他方式来实例化对象。那么我们首先将构造函数/拷贝构造函数/赋值运算符重载函数定义为私有。然后再在定义一个函数返回单例的地址。切记这个函数必须定义为静态的。倘若不设置成静态的,那么我们只有创建了对象才能调他,但是我们的初衷又是不要通过别的方式创建对象,这样就会产生矛盾

饿汉模式的优点简单

因为我们在程序启动时就设置好了单例,所以饿汉模式是线程安全的。

饿汉模式的缺点:

会拖慢程序启动的速度。

如果有多个单例类对象实例启动顺序不确定。

class Hungry

{

public:

static Hungry* GetInstance()//对外提供一个静态的成员函数,返回我们的单例

{

return &onlyInstance;

}

private:

static Hungry onlyInstance;//定义一个静态的实例

private://将其他可能产生实例的函数都弄成私有,设置为删除函数是C++11的新写法

Hungry() {};

Hungry(const Hungry&) = delete;

Hungry& operator=(const Hungry&) = delete;

};

Hungry Hungry::onlyInstance;//单例别忘了类外初始化

int main()

{

Hungry* a = Hungry::GetInstance();

}

懒汉模式

在用到的时候才去创建单例。例如:一个人吃完饭在,并不会直接将碗洗了,只有在下一次吃饭的时候才去洗碗

这种模式往往用在单利对象在创建时很耗时,占用资源,例如加载插件等等。这时候就会用到懒汉模式。

懒汉模式的优点:

第一次要使用单例的时候才去创建, 进程启动的时候无负载。

多个单例启动顺序可以自由控制多个单例启动顺序可以自由控制

懒汉模式缺点:代码复杂,需要考虑线程安全,由用户加锁

#include<iostream>

#include<mutex>

#include<thread>

using namespace std;

class Lazy

{

public:

static Lazy* GetInstance()//返回单例

{

if (OnlyInstance == nullptr)

{

m_mutex.lock();

if (OnlyInstance == nullptr)//?为什么要双层检查

{

OnlyInstance = new Lazy();

}

m_mutex.unlock();

}

return OnlyInstance;

}

private:

static Lazy* OnlyInstance;//单例指针

static mutex m_mutex;//互斥量

private:

Lazy() {};

Lazy(const Lazy&) {};

Lazy& operator=(const Lazy&) {};

};

Lazy* Lazy::OnlyInstance = nullptr;//初始化

mutex Lazy::m_mutex;

我们看到上边有层if判断那么为什么需要两层???

首先说第一层检查:

当一个线程运行到第一层检查,如果发现实例已经创建,就直接返回。

如果没有创建则通过第一层检查。

进入第一层检查后,我们让各个线程竞争锁,竞争到锁的线程(我们用A表示)来进行第二层检查

第二层检查:

当线程A获取锁后,再次判断,如果实例仍未被创建,则创建实例。如果实例已经被创建,那么就是上次拿到锁的线程(B)创建了实例(B创建的时候A一直阻塞在获得锁的地方,A并不知道B先于他获得了锁,如果不再次判断,那么仍然有可能创建多个实例)。返回即可。

给我留言

留言无头像?