c语言中如何确保一个程序是单例的_c++单例模式
前言
在设计模式中,单例模式是最简单的一种。如何确保让一个资源在使用中只能实例化一次呢?如何确保在多线程环境下是线程安全的呢?本文将从最简单的单例到线程安全的单例一一讲解。
一、单线程环境
以下是最起初的单例模式,声明一个静态实例,每次获取前先判断其是否创建,如果没有就创建,如果有就直接返回创建的对象。
class Singlton{ 单例模式下,不能使用拷贝构造、复制构造等函数,所以应将其声明为私有的。 c++11 提供delete 关键字,用于删除不适用的构造函数。private: Singlton(){}; Singlton(const Singlton& obj); Singleton& operator=(const Singleton&)=delete;static Singlton* instance;public: Singlton * Singlton::getInstance(){if(instance == null) instance = new Singlton();return instance; }};
二、多线程环境(锁代价过高)
由于上述代码只能运行在单线程环境,在多线程中应该使用锁来实现。
class Singlton{private: Singlton(){}; Singlton(const Singlton& obj); Singleton& operator=(const Singleton&)=delete;static std::mutex mx;static Singlton* instance;public: Singlton * Singlton::getInstance(){std::lockguard<std::mutex> lock(mx); if(instance == null) instance = new Singlton();return instance; }};
三、多线程环境双检查锁(reorder现象)
由于加锁会使得锁代价过高,每个线程都会阻塞在 mutex 处,直到获取实例。那么就提出了下面这种双检查锁,先判断当前是否有实例化,如果有直接返回,避免了等待所的过程。
class Singlton{private: Singlton(){}; Singlton(const Singlton& obj); Singleton& operator=(const Singleton&)=delete;static std::mutex mx;static Singlton* instance;public: Singlton * Singlton::getInstance(){if(instance == null){ 1std::lockguard<std::mutex> lock(mx); 2if(instance == null) 3 instance = new Singlton(); 4 }return instance; }};//因为在多线程下,只是对instance变量的读判断,而读是不需要加锁的,//在第一层检查时,如果另外一个线程读取instance不是null,就直接返回。
问题:因为现代编译器和 CPU 都会进行指令优化重排序,比如一句简单的指令 instance = new Singlton();程序员人为理解: 1、分配内存 2、调用类构造器初始化 3、将内存指针赋值给 instance;但 CPU 执行指令可能会是:1 --> 3 --> 2 的情况,这种现象将 reorder 重排序;那么当线程 t1 执行 3 的时候,线程 t2 判断 instance 不为空,直接返回,卧槽,使用一个为分配好的内存,灾难是无法想象的。
三、 解决多线程安全的单例
为了解决上述 reorder 现象,是能使用内存 fence 去组织编译器进行重排序,那么这将是真正意义上的线程安全的单例模式。
std::atomic Singlton::m_instance;std::mutex Singlton::m_mutex;Singlton* Singlton::getInstance(){ Singlton* tmp = m_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acquire);if(tmp == nullptr){std::lock_guard<std::mutex> lock(m_mutex); tmp = m_instance.load(std::memory_order_relaxed);if(tmp == nullptr){ tmp = new Singlton;std::amotic_thread_fence(std::memory_order_release); m_instance.store(tmp, std::memory_order_relaxed); } }return tmp;}
四、c++11的线程安全的单例
c++11中引入了 call_once ,这样就可以编写更加简单的单例模式了,以下是 c++11 的线程安全的单例模式。
#include #include #include class Singleton {public:static Singleton& GetInstance() {static std::once_flag s_flag;std::call_once(s_flag, [&]() { instance_.reset(new Singleton); });return *instance_; } ~Singleton() = default;private: Singleton() = default; Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;private:static std::unique_ptr instance_;};std::unique_ptr Singleton::instance_;
五、工程中那种好?
其实,需要根据项目的不同,应该选择适合自己的单例模式。注:在项目中,一般可将所有的单例在main函数中进行初始化,所有的以后操作都是读操作,既保证线程安全的单例模式
c语言中如何确保一个程序是单例的_c++单例模式相关推荐
- C语言中编写一个程序,提示用户输入两个日期,然后显示哪一个日期更早
文章目录 编写一个程序,提示用户输入两个日期,然后显示哪一个日期更早 #include<stdio.h> int main(){ int a,s,d; int z,x,c; scanf(& ...
- java数组输入一个实数_用java!!输入五个数,保存到一个数组中,然后将... C语言,编写一个程序,从键盘输入5个数,算出总和......
导航:网站首页 > 用java!!输入五个数,保存到一个数组中,然后将... C语言,编写一个程序,从键盘输入5个数,算出总和... 用java!!输入五个数,保存到一个数组中,然后将... C ...
- C语言中打开一个应用程序可以调用或运行命令
C语言中打开一个应用程序可以调用或运行命令system(),也可以调用操作系统的API函数,比如Windows系统可以调用CreateProcess().ShellExecuteEx()等函数来打开一 ...
- c语言算正方形面积和周长,c语言中编写一程序计算正方形的周长和面积
C语言 编写程序,从键盘输入一个正数,计算该数的平方根. #include#includeintmain(){\x09doublex;\x09scanf("%lf",&x) ...
- c语言程序设计 徐庆生,C语言中循环结构程序课的教学设计与探讨.doc
C语言中循环结构程序课的教学设计与探讨 摘要:循环结构的程序设计是面向过程的程序设计课程的核心部分,掌握好循环结构的程序设计技术对学好此类课程至关重要.本文重点介绍了"C语言程序设计&quo ...
- Java黑皮书课后题第5章:**5.45(统计:计算平均值和标准方差)在商务应用中……编写一个程序,提示用户输入10个数字,然后运用下面的公式,显示这些数字的平均值以及标准方差
5.45(统计:计算平均值和标准方差)在商务应用中--编写一个程序,提示用户输入10个数字,然后运用下面的公式,显示这些数字的平均值以及标准方差 题目 题目描述 破题 运行示例 代码 题目 题目描述 ...
- c语言判断字符串的编码,C语言中判断一个char*是不是utf8编码
C语言中判断一个char*是不是utf8编码 里我修改了一下, 纯ASCII编码的字符串也返回true, 因为UTF8和ASCII兼容 实例代码: int utf8_check(const char* ...
- 怎样设置一个函数C语言,C语言中怎样编写一个函数 如何在C语言中定义一个函数?...
如何在C语言中定义一个函数?小编很想在你面前流泪最后却还是选择装作打个哈欠 为什么小编怎么定义函数都不正确呢? 总是说小编 表达语法错误在main函数中 小编们可以在头文件与main函数之间定义,并编 ...
- c语言判断utf-8中文字符串,C语言中判断一个char*是不是utf8编码分享
--想了解C语言中判断一个char*是不是utf8编码分享的全部内容且更多的C语言教程关注 C语言中判断一个char*是不是utf8编码 里我修改了一下, 纯ASCII编码的字符串也返回true, 因 ...
最新文章
- 解决:angularjs radio默认选中失效问题
- Nature:AI为什么总是歧视重重?
- 《深入浅出WPF》笔记——事件篇
- php嵌入html后缀_php中怎么嵌入html代码
- C++——《数据结构与算法》实验——排序算法的实现
- Tomcat集群快速入门:Nginx负载均衡配置,常用策略,场景及特点
- @RequestMapping和@GetMapping @PostMapping 区别
- mysql空间是什么格式_MySQL数据类型 - 空间数据类型 (6)
- python静默打印pdf_前端静默打印实现 html pdf集合
- 人工智能、机器学习和深度学习的区别与认识
- 安卓3d游戏开发引擎_鲁大师安卓3D引擎更新,跑分测试精准度再升级
- 利用模态DIV结合UpdateProgress防止页面重复提交
- mysqldump 工具的使用
- filepath直接指定到文件名吗_按照txt中指定的文件名,从src_path中拷贝文件到dest_path(copyfile_from_txt)...
- 读懂电影专业名词(转自CMCT-PT)
- 关于GitHub Education(GitHub教育认证)认证
- 贝塞尔曲线 unity两点画曲线弧线三点
- indigo版本teb_local_planner常见编译问题
- 科目三必看要点 驾驶经验汇总
- U盘、打印机泄密的隐患
热门文章
- Docker 三剑客之 Docker Compose
- mysql_connect报告”No such file or directory”错误的解决方法
- [笔记]TB-6S-LX150T-IMG2_HWUserManual_1.02e实例讲解
- 驾考通专业版2011
- 07:有趣的跳跃【一维数组】
- apt-get install php5-redis,Ubuntu14-04安装redis和php5-redis扩展
- 10a 16a 插座区别_电动汽车小知识(NO·5):电动汽车能否用家里的插座进行充电?...
- 华为交换机模拟器_从零开始学习华为路由交换 | 配置缺省静态路由
- git安装步骤_详解linux安装git的方法步骤(超实用)
- Linux linux下的进程状态