前提:“对象性能”模式 

面向对象很好的解决了“抽象”的问题,但是必不可免地要付出一定的代价。
对于通常情况来讲,面向对象的成本大都可以忽略不计。但是某些情况,面向对象所带来的成本必须谨慎处理。
前面是利用抽象手段来实现松耦合的设计,但是抽象必不可免的会带来一定代价,比如虚函数(倍乘)

典型模式:

单件模式:Singleton
享元模式:Flyweight
只有这两个模式不是解决抽象问题,而是解决性能问题。

一:单例模式

(一)概念

保证了一个类只生成唯一的实例对象。保证一个类,只有一个实例存在,同时提供能对该实例加以访问的全局方法

(二)动机

在软件系统中,经常有这样一个特殊的类,必须保证它们在系统中只存在一个示例,才能确保他们的逻辑正确性、以及良好的效率。
如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?工厂模式绕过new是为了避开紧耦合,单例模式避开new,是解决性能问题
这个应该类设计者的责任,而不是使用者的责任。
解决方案:
.将构造函数设置为私有的
.提供一个全局的静态方法
.定义一个静态指针,指向本类的变量的静态变量指针

(三)代码讲解(四种版本)

1.线程非安全版本(单线程OK)

class Singleton{
private:
Singleton();
Singleton(const Singleton& other);
public:
static Singleton* getInstance();
static Singleton* m_instance;
}; Singleton* Singleton::m_instance=nullptr; //线程非安全版本
Singleton* Singleton::getInstance() {  //由于是在类的外部定义,也是静态方法,不用加static
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
多线程模式,当多个线程都进入到if中,会导致创建多个实例对象

2.线程安全版本,但是锁的代价过高

//线程安全版本,但锁的代价过高
Singleton* Singleton::getInstance() {
Lock lock;  //锁的局部变量,会在函数结束时自动释放,我们也可以设置为全局变量锁。局部变量锁可用是因为这个函数是静态方法,所以大家获取的还是一个锁
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
问题:高并发时,代价过高,对读线程的锁来说是浪费的。读线程本来应该直接跳过创建实例,直接去使用唯一实例,但是这里还有等待锁的释放,是不必要的。

3.双检测锁(锁前锁后检查),但由于内存读写reorder不安全

//双检查锁,但由于内存读写reorder不安全
Singleton* Singleton::getInstance() { if(m_instance==nullptr){
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
}
return m_instance;
}
解决了上面的问题,但是还有新的问题出现:内存读写reorder不安全

reorder不安全::代码执行顺序不确定

假象顺序:

 分配内存; 调用构造器; 返回指针

CPU层面指令集(有可能reorder)<时间片的获取>:

1分配内存;2返回指针 m_instance ;3调用构造器
(执行第2步之后,另外一个线程进来发现m_instance不是null,但未执行构造器,对象状态不正确,而之前获取m_instance的线程已经开始使用了对象,会出错)

4.C++11版本之后的跨平台实现(volatile)

//C++ 11版本之后的跨平台实现 (volatile)
std::atomic<Singleton*> Singleton::m_instance;  //原子对象
std::mutex Singleton::m_mutex;   Singleton* Singleton::getInstance() {
Singleton* tmp = m_instance.load(std::memory_order_relaxed);  //屏蔽编译器reorder
std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
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);//释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);  //存放变量回去
}
}
return tmp;
}

四种版本场景使用:

版本一:单线程可用,足够好
版本二:多线程可用,不算错,代价过高
版本三:不能用,所有编译器例出问题概率较高
版本四:C++11前各个平台不同实现

(四)模式定义

保证一个类仅有一个实例,并提供一个该实例的全局访问点。
——《设计模式》GoF

(五)类图(结构)

(六)要点总结

1.Singleton模式中的实例构造器可以设置为protected以允许子类派生。

2.Singleton模式一般不要支持拷贝构造函数和Clone接口,因为这有可能会导致多个对象实例,与Singleton模式的初衷相违背。

3.如何实现多线程环境下安全的Singleton?注意对双检查锁的正确实现。

(七)案例实现

1.单线程版本

#include <iostream>
#include <stdlib.h>
using namespace std; class Singleton
{
private:
static Singleton* m_singer;
static int count;
private:
Singleton()
{
m_singer = NULL;
count = ;
cout << "contruct function exec" << endl;
}
public:
static Singleton* getInstance()
{
if (m_singer==NULL)
{
m_singer = new Singleton();
count++;
}
return m_singer;
} static void printInfo()
{
cout << "create object " << count << endl;
}
}; Singleton* Singleton::m_singer = NULL; //懒汉模式,并没有创建对象
int Singleton::count = ; void main()
{
cout << "Lazy pattarn:" << endl;
Singleton* p1 = Singleton::getInstance();
Singleton* p2 = Singleton::getInstance();
if (p1!=p2)
{
cout << "not the same object" << endl;
}
else
{
cout << "the same object" << endl;
}
p1->printInfo();
p2->printInfo(); system("pause");
return;
}

懒汉模式

Singleton* Singleton::m_singer = NULL;

#include <iostream>
#include <stdlib.h>
using namespace std; class Singleton
{
private:
static Singleton* m_singer;
static int count;
private:
Singleton()
{
m_singer = NULL;
count = ;
cout << "contruct function exec" << endl;
}
public:
static Singleton* getInstance()
{
return m_singer;
} static void printInfo()
{
cout << "create object " << count << endl;
} static void FreeInstance()
{
if (m_singer!=NULL)
{
delete m_singer;
m_singer = NULL;
count = ;
}
}
}; Singleton* Singleton::m_singer = new Singleton(); //饿汉模式,不管你创不创建实例,先给你实例了再说
int Singleton::count = ; void main()
{
cout << "Starve pattarn:" << endl;
Singleton* p1 = Singleton::getInstance();
Singleton* p2 = Singleton::getInstance();
if (p1!=p2)
{
cout << "not the same object" << endl;
}
else
{
cout << "the same object" << endl;
} p1->printInfo();
p2->printInfo(); p1->FreeInstance();
p2->FreeInstance(); //不会报错,因为我们会做if判断 system("pause");
return;
}

饿汉模式

Singleton* Singleton::m_singer = new Singleton();

补充:在两个模式的类的private中加上

    Singleton(const Singleton& obj){}    //防止拷贝构造
Singleton& operator=(const Singleton& obj){} //防止赋值

2.多线程版本,使用锁(讨论懒汉模式,饿汉无影响)

未使用锁的多线程

#include <iostream>
#include <windows.h>
#include <winbase.h>
#include <process.h>
#include <stdlib.h>
using namespace std; class Singleton
{
private:
static Singleton* m_singer;
static int count;
private:
Singleton()
{
cout << "contruct function exec begin" << endl;
m_singer = NULL;
count = ;
Sleep();
cout << "contruct function exec finished" << endl;
} Singleton(const Singleton& obj){} //防止拷贝构造
Singleton& operator=(const Singleton& obj){} //防止赋值 public:
static Singleton* getInstance()
{
if (m_singer==NULL)
{
m_singer = new Singleton();
count++;
}
return m_singer;
} static void printInfo()
{
cout << "create object " << count << endl;
} static void FreeInstance()
{
if (m_singer != NULL)
{
delete m_singer;
m_singer = NULL;
count = ;
}
}
}; Singleton* Singleton::m_singer = NULL;
int Singleton::count = ; void threadfunc(void *mylpAdd)
{
int id = GetCurrentThreadId();
cout << "ThreadID: " << id << endl;
Singleton::getInstance()->printInfo(); //线程中获取一个实例对象
} unsigned __stdcall threadfunc2(void *mylpAdd)
{
int id = GetCurrentThreadId();
cout << "ThreadID: " << id << endl;
Singleton::getInstance()->printInfo(); //线程中获取一个实例对象
return ;
} void main()
{ int i = ;
unsigned int dwThreadId[], dwThrdParam = ;
HANDLE hThread[];
int threadnum = ; //生成线程
for (i = ; i < threadnum;i++)
{
hThread[i] = (HANDLE)_beginthreadex(NULL, , &threadfunc2, NULL, , &dwThreadId[i]);
//hThread[i] = (HANDLE)_beginthread(&threadfunc, 0, NULL);
if (hThread[i]==NULL)
{
cout << "product thread failure" << endl;
break;
}
}
//等待线程
for (i = ; i < threadnum;i++)
{
WaitForSingleObject(hThread[i], INFINITE);
} cout << "wait for threadings end" << endl;
//删除
for (i = ; i < threadnum;i++)
{
CloseHandle(hThread[i]);  //注意CloseHandle只能和_beginthreadex使用,使用_beginthread时不需要这个
} Singleton::FreeInstance();
system("pause");
return;
}

我们发现多线程创建实例是不按顺序,谁先抢占时间片谁就先来
线程内核对象的内部初始使用计数为2 从线程函数返回时会递减使用计数. 此时线程已经终止,但线程内核对象的使用计数为1 线程内核对象并不会释放.
调用_beginthreadex线程终止时使用计数为1 调用_beginthread终止时使用计数由于_endthread内部调用了CloseHandle使用计数变为0
释放线程内核对象,而_beginthreadex中线程终止时使用计数为1 要显示调用CloseHandle才会释放.

使用锁的多线程

#include <iostream>
#include <windows.h>
#include <winbase.h>
#include <process.h>
#include <stdlib.h>
using namespace std; class CMyCritical  //实现了对锁的封装
{
public:
CMyCritical()
{
InitializeCriticalSection(&m_sec);
}
~CMyCritical()
{
DeleteCriticalSection(&m_sec);
}
void Lock()
{
EnterCriticalSection(&m_sec);
}
void Unlock()
{
LeaveCriticalSection(&m_sec);
}
private
:
CRITICAL_SECTION m_sec;
};


//临界区,线程锁
static CMyCritical cs; //是对关键段CRITICAL_SECTION的封装
class Singleton
{
private:
static Singleton* m_singer;
static int count;
private:
Singleton()
{
cout << "contruct function exec begin" << endl;
m_singer = NULL;
count = ;
Sleep();
cout << "contruct function exec finished" << endl;
} Singleton(const Singleton& obj){} //防止拷贝构造
Singleton& operator=(const Singleton& obj){} //防止赋值 public:
static Singleton* getInstance()
{
cs.Lock();
if (m_singer==NULL)
{
m_singer = new Singleton();
count++;
}
cs.Unlock();
return m_singer;
} static void printInfo()
{
cout << "create object " << count << endl;
} static void FreeInstance()
{
if (m_singer != NULL)
{
delete m_singer;
m_singer = NULL;
count = ;
}
}
}; Singleton* Singleton::m_singer = NULL;
int Singleton::count = ; void threadfunc(void *mylpAdd)
{
int id = GetCurrentThreadId();
cout << "ThreadID: " << id << endl;
Singleton::getInstance()->printInfo(); //线程中获取一个实例对象
} unsigned __stdcall threadfunc2(void *mylpAdd)
{
int id = GetCurrentThreadId();
cout << "ThreadID: " << id << endl;
Singleton::getInstance()->printInfo(); //线程中获取一个实例对象
return ;
} void main()
{ int i = ;
unsigned int dwThreadId[], dwThrdParam = ;
HANDLE hThread[];
int threadnum = ; //生成线程
for (i = ; i < threadnum;i++)
{
hThread[i] = (HANDLE)_beginthreadex(NULL, , &threadfunc2, NULL, , &dwThreadId[i]);
//hThread[i] = (HANDLE)_beginthread(&threadfunc, 0, NULL);
if (hThread[i]==NULL)
{
cout << "product thread failure" << endl;
break;
}
}
//等待线程
for (i = ; i < threadnum;i++)
{
WaitForSingleObject(hThread[i], INFINITE);
} cout << "wait for threadings end" << endl;
//删除
for (i = ; i < threadnum;i++)
{
CloseHandle(hThread[i]);
} Singleton::FreeInstance();
system("pause");
return;
}

注意:我们不能直接使用CCriticalSection,这是MFC类库中的,我们要先引入

3.C++11版本

#include <iostream>
#include <windows.h>
#include <winbase.h>
#include <process.h>
#include <stdlib.h>
#include <mutex>
#include <atomic>
#include <thread>
using namespace std; class Singleton; std::atomic<Singleton*> m_install; //原子对象
std::mutex m_mutex;
class Singleton
{
private:
static Singleton* m_singer;
static int count;
private:
Singleton()
{
cout << "contruct function exec begin" << endl;
m_singer = NULL;
count = ;
Sleep();
cout << "contruct function exec finished" << endl;
} Singleton(const Singleton& obj){} //防止拷贝构造
Singleton& operator=(const Singleton& obj){} //防止赋值 public:
static Singleton* getInstance()
{
Singleton* tmp = m_install.load(std::memory_order_relaxed); //屏蔽编译器reorder
std::atomic_thread_fence(std::memory_order_acquire); //获取内存fence
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex); //锁的设置
tmp = m_install.load(std::memory_order_relaxed); //取变量出来
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存fence
m_install.store(tmp, std::memory_order_relaxed); //存放变量回去
}
}
return tmp;
}
static void printInfo()
{
cout << "create object " << count << endl;
} static void FreeInstance()
{
if (m_singer != NULL)
{
delete m_singer;
m_singer = NULL;
count = ;
}
}
}; Singleton* Singleton::m_singer = NULL;
int Singleton::count = ; void threadfunc(void *mylpAdd)
{
int id = GetCurrentThreadId();
cout << "ThreadID: " << id << endl;
Singleton::getInstance()->printInfo(); //线程中获取一个实例对象
} unsigned __stdcall threadfunc2(void *mylpAdd)
{
int id = GetCurrentThreadId();
cout << "ThreadID: " << id << endl;
Singleton::getInstance()->printInfo(); //线程中获取一个实例对象
return ;
} void main()
{ int i = ;
unsigned int dwThreadId[], dwThrdParam = ;
HANDLE hThread[];
int threadnum = ; //生成线程
for (i = ; i < threadnum;i++)
{
hThread[i] = (HANDLE)_beginthreadex(NULL, , &threadfunc2, NULL, , &dwThreadId[i]);
//hThread[i] = (HANDLE)_beginthread(&threadfunc, 0, NULL);
if (hThread[i]==NULL)
{
cout << "product thread failure" << endl;
break;
}
}
//等待线程
for (i = ; i < threadnum;i++)
{
WaitForSingleObject(hThread[i], INFINITE);
} cout << "wait for threadings end" << endl;
//删除
for (i = ; i < threadnum;i++)
{
CloseHandle(hThread[i]);
} Singleton::FreeInstance();
system("pause");
return;
}

设计模式---对象性能模式之单例模式(Singleton)的更多相关文章

  1. 设计模式---对象性能模式之享元模式(Flyweight)

    一:概念 通过与其他类似对象共享数据来减少内存占用 如果一个应用程序使用了太多的对象, 就会造成很大的存储开销. 特别是对于大量轻量级 (细粒度)的对象,比如在文档编辑器的设计过程中,我们如果为每个字 ...

  2. C++设计模式 之 “对象性能” 模式:Singleton、Flyweight

    “对象性能”模式 面向对象很好地解决了“抽象”的问题,但是必不可免地要付出一定的代价.对于通常情况来讲,面向对象的成本大都可以忽略不计.但是某些情况,面向对象所带来的成本必须谨慎处理. 典型模式 # ...

  3. 学习记录:《C++设计模式——李建忠主讲》5.“对象性能”模式

    对象性能模式:面向对象很好地解决了抽象地问题,但是必不可免地要付出一定地代价.对于通常情况来讲,面向对象地成本大都可以忽略不计,但某些情况,面向对象所带来地成本必须谨慎处理. 典型模式:单件模式(Si ...

  4. 23种设计模式 - 对象性能(Singleton - Flyweight享元)

    其他设计模式 23种设计模式(C++) 每一种都有对应理解的相关代码示例 → Git原码 ⌨ 对象性能 面向对象很好地解决了"抽象"的问题,但是必不可免地付出一定的代价.对于通常情 ...

  5. PHP 设计模式 笔记与总结(6)基础设计模式:工厂模式、单例模式和注册树模式

    三种基础设计模式(所有面向对象设计模式中最常见的三种): ① 工厂模式:使用工厂方法或者类生成对象,而不是在代码中直接new 在 Common 目录下新建 Factory.php: <?php ...

  6. java中单态模式或单例模式(Singleton)有什么意义?

    8.单态模式或单例模式(Singleton) 单态模式有什么用呢?想一下Adobe Photoshop ,处理两张图,会启动两个photoshop吗?多耗费内存呀! ( Consider Adobe ...

  7. 设计模式---对象创建模式之原型模式(prototype)

    一:概念 原型模式(Prototype Pattern) 实际上就是动态抽取当前对象运行时的状态 Prototype模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例.使用Protot ...

  8. 设计模式---对象创建模式之工厂方法模式(Factory Method)

    前提:“对象创建”模式 通过“对象创建”模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定.它是接口抽象之后的第一步工作. 典型模式(表现最为突出) 工 ...

  9. &quot;围观&quot;设计模式(7)--创建型之单例模式(Singleton Pattern)

    单例模式,也叫单子模式,是一种经常使用的软件设计模式.在应用这个模式时,单例对象的类必须保证仅仅有一个实例存在. 很多时候整个系统仅仅须要拥有一个的全局对象.这样有利于我们协调系统总体的行为.比方在某 ...

随机推荐

  1. POJ 3020 -Antenna Placement-二分图匹配

    题意:一个N*M的矩阵里有K个观测点,你必须放置天线覆盖所有观测点.每个雷达只能天线两个观测点,这两点必须相邻.计算最少天线数. 做法:将所有相邻的观测点连起来,建图.跑一遍匈牙利算法就计算出了最大的 ...

  2. Python中第三方模块requests解析

    一.简述 Requests HTTP Library 二.模块框架 ''' __version__ _internal_utils adapters api auth certs compat coo ...

  3. Codeforces1071C Triple Flips 【构造】【Four Russians】

    题目分析: 这种题目显然可以先考虑哪些无解.我们发现我们不考虑操作次数的时候,我们可以选择连续的三个进行异或操作. 这样我们总能使得一个序列转化为$000...000xy$的形式.换句话说,对于$00 ...

  4. selenium+python启动Firefox浏览器失败问题和点击登陆按钮无效问题

    问题1:使用python+selenium编写脚本调用Firefox时报错:

  5. 「CodeForces - 717E」Paint it really, really dark gray (dfs)

    BUPT 2017 summer training (for 16) #1H 题意 每个节点是黑色or白色,经过一个节点就会改变它的颜色,一开始在1节点.求一条路径使得所有点变成黑色. 题解 dfs时 ...

  6. [CF976E]Well played!

    题目描述 Recently Max has got himself into popular CCG "BrainStone". As "BrainStone" ...

  7. base64加密图片处理

    场景:下载html中内嵌的base64加密图片 举个例子,博客园的插入图片有两种方式,一是引用图片链接,二是直接粘贴2进制图片文件.以第二种方式的图片则是以base64加密的方式内嵌在html页面中. ...

  8. hdu1394逆序数(线段树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题目大意:逆序数:即假设在数组a中,假如i<j,但是a[i]>a[j]. 现在有一个 ...

  9. 简明的Python教程中的几个疑惑点分析#2

    #1简明的Python教程当中第十四章Python标准库介绍中的使用sys模块 假如你没看懂sys模块这一章节,那么没关系,看下面一段代码再看简明的Python教程中的sys模块实例你将很容易看懂 代 ...

  10. express搭建服务器

    学习express搭建node服务器 一.安装express框架 1.了解框架(百度) 2.下载框架 (1)使用npm命令下载 npm install express -g //全局安装,安装的是ex ...