先看一个最简单的教科书式单例模式:
class CSingleton
{
public:
static CSingleton* getInstance()
{
if (NULL == ps)
{//tag1
ps = new CSingleton;
}
return ps;
}
private:
CSingleton(){}
CSingleton & operator=(const CSingleton &s);
static CSingleton* ps;
};
CSingleton* CSingleton::ps = NULL;
有2个要点:
1.private的构造函数和=操作符,用于防止类外的实例化和被复制;
2.static的类指针和get方法。
在大多数单线程情况下,以上代码大都会运行得很好,除非遇到中断:
1.当程序运行到tag1 处触发了中断;
2.中断处理程序恰调用的也是getInstance函数。
可想而知,这和多线程的情况类似,假设线程A 运行到tag1处,还没来得及new,此时ps仍然是NULL,线程B(或中断处理程序) 同时也运行到此通过if判断,那么将会实例化2个CSingleton对象,显然是不对的。
为了解决上述问题,自然而然,最容易想到也最常用的方法是加锁,因此getInstance改成这样:
static CSingleton* getInstance()
{
lock();//伪代码
if (NULL == ps)
{
ps = new CSingleton;
}
return ps;
}
加了锁以后貌似解决了上述问题,但也同样带来了新的问题:如果程序到处是诸如:
CSingleton::instance()->aaaa(); CSingleton::instance()->bbbb(); CSingleton::instance()->cccc();
这样的调用,除了第一次的lock()有用外,后面的都是在做无用功,lock()的代价说大不大,但在某些情况下还是会提高程序延迟,这对追求完美的程序猿来说是完全无法接受的。
于是乎,咱想出了一个办法:
static CSingleton* getInstance()
{
if (NULL == ps)//这里加了次判断,只有第一次才会为true而调用lock()
{
lock();//伪代码
if (NULL == ps)
{
ps = new CSingleton;
}
}
return ps;
}
很久以后我才知道,这个方法有个很高大上的名字,叫做双重检查锁定模式,简称DCLP(Double Checked Locking Pattern)。
DCLP很好地解决了多次调用不必要的lock()。
然而,你们以为这样就完了?too young。。
DCLP在多线程下仍然存在2个根本问题:
1.程序的指令执行顺序不确定;
2.编译器优化问题。
先说2,在某些编译器下,以上的两个if判断只会执行一个,甚至一个都不执行,原因是编译器认为至少有一个if判断是多余的,它自动帮助我们优化了代码。
再说1,ps = new CSingleton; 这条语句会被拆分为这样的三个步骤执行:
1.为要new的对象开辟一块内存;
2.构造该对象,填入这块内存;
3.将ps指针指向这块内存。
以上三个步骤,2和3的顺序是不确定的,可能先2后3,也可能先3后2。。。
实际执行时可能是这样的:
static CSingleton* getInstance()
{
if (NULL == ps)
{
lock();//伪代码
if (NULL == ps)
{ //伪代码
ps = xx;//step 3
new sizeof(CSingleton);//step 1
new CSingleton;//step 2
}
}
return ps;
}
如果编译器按上述顺序执行代码,考虑如下状况:
线程A 执行到step 1还未执行后面的step 2,此时ps非空,但其指向的内存里面的内容还未被构造出来,于此同时线程B 进入这个函数,判断ps非空直接返回ps,但是调用者此时访问的ps内存实际内容CSingleton还没被构造呢,这是一块地址正确大小正确但内部数据不明的东西,当然会出错(调用者一般这么调用:CSingleton::getInstance()->aa(); CSingleton::getInstance()->bb(); CSingleton::getInstance()->cc();........此时的aa,bb,cc是啥玩意儿?)。
这也是为什么加上volatile关键字仍然不可以解决同步问题,volatile只解决了编译器优化问题,却无法控制机器指令执行顺序。
很遗憾的是,C/C++本身在设计时是不考虑多线程问题的,也就是说,要处理多线程问题还要程序猿自己想办法填坑。。
说了这么多,我们要讨论的问题仍然没有解决,庆幸的是,C++ 11提供了内存栅栏技术来解决这个问题,这里不赘述,有兴趣的读者可以自己搜索资料看看,不过是一些api调罢了。
那么,C++ 11 以前的代码如何解决这个问题呢?很不幸,并没有很好的解决方案,一种可行的方案是,程序中不要到处这么调用这个单例对象:
CSingleton::getInstance()->aa(); CSingleton::getInstance()->bb(); CSingleton::getInstance()->cc();
而是在程序开始就初始化缓存这个单例对象:
CSingleton* const g_ps = CSingleton::getInstance();//程序一开始就缓存这个单例对象 g_ps->aa(); g_ps->bb(); g_ps->cc();
但是如此带来的问题是程序一开始就实例化了这个单例对象,对象在整个程序的声明周期存在,这貌似叫饿汉式,而之前那种叫懒汉式,孰轻孰重,只有根据实际情况取舍了。
以上就是小编为大家带来的从C++单例模式到线程安全详解全部内容了,希望大家多多支持~
# c
# 单例模式
# 线程安全
# 详解C++实现线程安全的单例模式
# C++线程安全的单例模式讲解
# 老生常谈C++的单例模式与线程安全单例模式(懒汉/饿汉)
# 详解如何使用C++写一个线程安全的单例模式
# 很好
# 多线程
# 的是
# 是在
# 还没
# 解决了
# 这块
# 不确定
# 解决这个问题
# 这是
# 情况下
# 都不
# 有个
# 将会
# 是这样
# 调用者
# 说了
# 这么多
# 不可以
# 但在
相关文章:
海南网站制作公司有哪些,海口网是哪家的?
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
浙江网站制作公司有哪些,浙江栢塑信息技术有限公司定制网站做的怎么样?
历史网站制作软件,华为如何找回被删除的网站?
建站主机服务器选型指南与性能优化方案解析
nginx修改上传文件大小限制的方法
c++怎么用jemalloc c++替换默认内存分配器【性能】
平台云上自主建站:模板化设计与智能工具打造高效网站
图册素材网站设计制作软件,图册的导出方式有几种?
大连网站设计制作招聘信息,大连投诉网站有哪些?
如何选择美橙互联多站合一建站方案?
如何快速登录WAP自助建站平台?
建站主机是什么?如何选择适合的建站主机?
ppt在线制作免费网站推荐,有什么下载免费的ppt模板网站?
如何在建站主机中优化服务器配置?
seo网站制作优化,网站SEO优化步骤有哪些?
广德云建站网站建设方案与建站流程优化指南
制作网站的模板软件,网站怎么建设?
如何在Golang中使用replace替换模块_指定本地或远程路径
如何高效完成自助建站业务培训?
如何选择高效稳定的ISP建站解决方案?
定制建站方案优化指南:企业官网开发与建站费用解析
linux top下的 minerd 木马清除方法
Thinkphp 中 distinct 的用法解析
如何选购建站域名与空间?自助平台全解析
手机网站制作与建设方案,手机网站如何建设?
XML的“混合内容”是什么 怎么用DTD或XSD定义
手机怎么制作网站教程步骤,手机怎么做自己的网页链接?
如何通过.red域名打造高辨识度品牌网站?
微信推文制作网站有哪些,怎么做微信推文,急?
建站主机选购指南:核心配置与性价比推荐解析
如何设置并定期更换建站之星安全管理员密码?
如何通过NAT技术实现内网高效建站?
高性能网站服务器部署指南:稳定运行与安全配置优化方案
微信小程序 五星评分(包括半颗星评分)实例代码
大连企业网站制作公司,大连2025企业社保缴费网上缴费流程?
如何规划企业建站流程的关键步骤?
网站制作网站,深圳做网站哪家比较好?
建站之星CMS五站合一模板配置与SEO优化指南
高防服务器租用如何选择配置与防御等级?
深圳网站制作培训,深圳哪些招聘网站比较好?
公司门户网站制作流程,华为官网怎么做?
建站VPS选购需注意哪些关键参数?
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
专业网站建设制作报价,网页设计制作要考什么证?
c++怎么使用类型萃取type_traits_c++ 模板元编程类型判断【方法】
企业网站制作费用多少,企业网站空间一般需要多大,费用是多少?
代购小票制作网站有哪些,购物小票的简要说明?
如何通过虚拟主机空间快速建站?
建站之星免费版是否永久可用?
*请认真填写需求信息,我们会在24小时内与您取得联系。