设计模式之单例模式与享元模式

设计模式之单例模式与享元模式

摘要

  单例模式(Single ton)与享元模式(Flyweight)归为对象性能模式。

1. 单例模式

  顾名思义,单例就是单实例,有些类需要限定在一个进程内只能存在一个实例。举个例子:一款游戏需要处理键鼠消息,我们设计了一个类封装了所有的方法,那这个类有且只能创建唯一的一个对象来处理用户的键鼠信息。

  单例模式的实现如果只考虑单线程的话so easy!但是涉及到多线程,难度一下就上来了。

#include <mutex>
/方案一 单线程版本/
/// <summary>
/// 单例类 可以把这个改成基类,子类继承来实现纯虚函数 getInstance,以提高代码复用
/// 最好的方法是新增泛型参数来实现服用
/// </summary>
class Singleton{
private:
    Singleton();
    Singleton(const Singleton& other);
public:
    static Singleton* getInstance();
    static Singleton* m_instance;
};

Singleton* Singleton::m_instance=nullptr;

// 线程非安全版本
// 何为线程非安全?
// 就是多线程cpu,存在A线程和B线程并发执行,同时new的话,就会对同一个静态变量m_instacnce进行写
Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}

/方案二 多线程版本//
// 线程安全版本,但锁的代价过高,因为一些读操作都加锁了
// 如何保证线程安全?
// 简单的方法是创建临界区,就是加锁

class Singleton {
private:
    Singleton();
    Singleton(const Singleton& other);
public:
    static Singleton* getInstance();
    static Singleton* m_instance;
    static std::mutex m_mutex;

};

Singleton* Singleton::getInstance() {
	std::unique_lock<std::mutex> lock(Singleton::m_mutex);
	lock.lock();//加锁之后也要判空,因为存在多线程同时执行到改行!所以里面的if判断必须要有
	if (m_instance == nullptr)
	{
		m_instance = new Singleton();
	}
	lock.unlock();
	return m_instance;
}


// ****以下的代码是不能用的,因为双检查锁有个编译器bug,即new可能先返回地址,构造器后构造对象,这样的话其他线程可能拿到
// 无法用的对象。因此如果是C#语言则需要加volatile关键字关闭编译器reorder
Singleton* Singleton::getInstance() {
    std::unique_lock<std::mutex> lock(Singleton::m_mutex);
    if (m_instance == nullptr) {
        lock.lock();//加锁之后也要判空,因为存在多线程同时执行到改行!所以里面的if判断必须要有
        if (m_instance == nullptr)
        {
            m_instance = new Singleton();
        }
        lock.unlock();
    }
    return m_instance;
}

///方案三 多线程跨平台版本/
class Singleton {
private:
    Singleton();
    Singleton(const Singleton& other);
public:
    static Singleton* getInstance();
    static std::atomic<Singleton*> m_instance;//实例是原子类里的一个对象
    static std::mutex m_mutex;

};

std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);//内存栅栏是acuire模式,也就是内存的原子操作顺序限制为:先store后store
    if (tmp == nullptr) 
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

  以上代码的方案三需要编译器支持C++11,涉及原子操作,memory_order参数,若有不懂的可以参考这篇文章:链接: link

2. 享元模式

  享元英文单词是flyweight,翻译过来是轻量级的意思,解决的是对象复用的问题,比如文档处理软件,如果里面的每个字都是一个对象的话,肯定有很多的重复对象,如何做到对象复用就是该模式解决的问题。

#include <map>
#include <string>
using namespace std;
class Font {
private:

    //unique object key
    string key;
    
    //object state
    //....
    
public:
    Font(const string& key){
        //...
    }
    void Draw()
    {
        //该字体对应的绘制函数
    };
};

/// <summary>
/// FontFactory 这个类管理程序中的所有Font类对象,如果要用到Font类对象,则在这个map里面查就可以了,如果没查到,则会自动新建一个。
/// 引出的思考:
/// 1、Font对象如果使用过程中发生了修改怎么办?难道要更新map?
/// 2、Font的构造函数如果很复杂,用这种模式是否还适合?
/// 3、多线程访问是不安全的吧?
/// </summary>
class FontFactory{
private:
    map<string,Font* > fontPool;
    
public:
    Font* GetFont(const string& key)
    {
        std::map<string,Font*>::iterator item=fontPool.find(key);
        if( item!= fontPool.end() )
        {
            return item->second;
        }
        else{
            Font* pFont = new Font(key);
            fontPool[key]= pFont;
            return pFont;
        }
    }
    
    void clear(){
        //...
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值