当前位置: 首页 > news >正文

C++11并发与多线程笔记(7) 单例设计模式共享数据分析、解决,call_once

C++11并发与多线程笔记(7) 单例设计模式共享数据分析、解决,call_once

  • 1.设计模式
  • 2.单例设计模式:
  • 3.单例设计模式共享数据分析、解决
  • 4.std::call_once():

1.设计模式

  • 程序灵活,维护起来可能方便,用设计模式理念写出来的代码很晦涩,但是别人接管、阅读代码都会很痛苦
  • 老外应付特别大的项目时,把项目的开发经验、模块划分经验,总结整理成设计模式
  • 中国零几年设计模式刚开始火时,总喜欢拿一个设计模式往上套,导致一个小小的项目总要加几个设计模式,本末倒置
  • 设计模式有其独特的优点,要活学活用,不要深陷其中,生搬硬套

2.单例设计模式:

整个项目中,有某个或者某些特殊的类,只能创建一个属于该类的对象
单例类:只能生成一个对象。

# include<iostream>
using namespace std;
class MyClass {//单例类
private:MyClass(){}//私有化构造函数static MyClass* m_instance;//静态成员变量
public:static MyClass* getInstance() {//静态成员函数用于创建对象if (m_instance == NULL) {m_instance = new MyClass();}return m_instance;}void func() {cout << "测试" << endl;}};
MyClass* MyClass::m_instance = NULL;//类内定义,类外初始化int main() {//创建一个对象,返回该类(Myclass)对象的指针MyClass* p_a = MyClass::getInstance();p_a->func();//测试
}

如果觉得在单例模式new了一个对象,而没有自己delete掉,这样不合理。可以增加一个类中类CGarhuishou,new一个单例类时创建一个静态的CGarhuishou对象,这样在程序结束时会调用CGarhuishou的析构函数,释放掉new出来的单例对象。

# include<iostream>
using namespace std;
class MyClass {//单例类
private:MyClass(){}//私有化构造函数static MyClass* m_instance;//静态成员变量
public:static MyClass* getInstance() {//静态成员函数用于创建对象if (m_instance == NULL) {m_instance = new MyClass();static CGarhuishou cl; //释放对象}return m_instance;}class CGarhuishou {//类中套类,用来释放对象public:~CGarhuishou() {if (MyClass::m_instance) {delete MyClass::m_instance;MyClass::m_instance = NULL;}}};void func() {cout << "测试" << endl;}};
MyClass* MyClass::m_instance = NULL;//类内定义,类外初始化int main() {//创建一个对象,返回该类(Myclass)对象的指针MyClass* p_a = MyClass::getInstance();p_a->func();//测试
}

3.单例设计模式共享数据分析、解决

面临问题:需要在自己创建的线程(而不是主线程)中来创建单例类的对象,这种线程可能不止一个。我们可能面临getInstance()这种成员函数需要互斥。
解决方法:可以在
加锁前判断m_instance是否为空
,否则每次调用MyClass::getInstance()都要加锁,十分影响效率。

# include<iostream>
# include<thread>
#include<mutex>
using namespace std;
mutex mutex1;
class MyClass {//单例类
private:MyClass(){}//私有化构造函数static MyClass* m_instance;//静态成员变量
public:static MyClass* getInstance() {//双重锁定 提高效率if (m_instance == NULL) {unique_lock<mutex> myMutex(mutex1);//单独加此代码,每一次判断创建新对象,都要加锁if (m_instance == NULL) {m_instance = new MyClass();static CGarhuishou cl;}}return m_instance;}class CGarhuishou {//类中套类,用来释放对象public:~CGarhuishou() {if (MyClass::m_instance) {delete MyClass::m_instance;MyClass::m_instance = NULL;}}};void func() {cout << "测试" << endl;}
};
MyClass* MyClass::m_instance = NULL;//类内定义,类外初始化//线程入口函数
void mythread() {cout << "myPrint线程开始执行了 " << endl;MyClass* p_a = MyClass::getInstance();cout << "myPrint线程结束执行了"  << endl;
}
int main() {thread myobj1(mythread);thread myobj2(mythread);myobj1.join();myobj2.join();
}

4.std::call_once():

函数模板,该函数的第一个参数标记第二个参数是一个函数名(如a())。
功能:能够保证函数a()只被调用一次。具备互斥量的能力,而且比互斥量消耗的资源更少,更高效。

  • call_once()需要与一个标记结合使用,这个标记为std::once_flag;其实once_flag是一个结构,call_once()就是通过标记来决定函数是否执行,调用成功后,就把标记设置为一种已调用状态

  • 多个线程同时执行时,使用call_once(),一个线程会等待另一个线程先执行。

# include<iostream>
# include<thread>
#include<mutex>
using namespace std;
once_flag g_flag;//标记来决定函数是否执行class MyClass {//单例类
private:MyClass() {}//私有化构造函数static MyClass* m_instance;//静态成员变量
public://用于call_once函数的第二个参数,保证其只被调用一次static void CreateInstance() {m_instance = new MyClass;static CGarhuishou cl;}//两个线程同时执行到这里,其中一个线程要等另外一个线程执行完毕static MyClass* getInstance() {call_once(g_flag,CreateInstance);return m_instance;}class CGarhuishou {//类中套类,用来释放对象public:~CGarhuishou() {if (MyClass::m_instance) {delete MyClass::m_instance;MyClass::m_instance = NULL;}}};void func() {cout << "测试" << endl;}
};
MyClass* MyClass::m_instance = NULL;//类内定义,类外初始化//线程入口函数
void mythread() {cout << "myPrint线程开始执行了 " << endl;MyClass* p_a = MyClass::getInstance();cout << "myPrint线程结束执行了"  << endl;
}
int main() {thread myobj1(mythread);thread myobj2(mythread);myobj1.join();myobj2.join();
}
http://www.lryc.cn/news/134284.html

相关文章:

  • FANUC机器人加减速倍率指令ACC的使用方法说明
  • 奥威BI数据可视化工具:360度呈现数据,告别枯燥表格
  • C# Linq源码分析之Take (三)
  • Linux journalctl命令详解(journalctl指令)(systemd服务默认日志管理工具)
  • 学习内容--
  • Stable Diffusion:使用自己的数据集微调训练LoRA模型
  • 软考高级系统架构设计师系列之:论文典型试题写作要点和写作素材总结系列文章一
  • 06 mysql all查询 和 主键查询 和 非索引列查询
  • 黑马点评-项目集成git及redis实现短信验证码登录
  • mac苹果电脑怎么运行Windows软件?怎么安装Win虚拟机?
  • Jmeter对websocket进行测试
  • 从2023年世界机器人大会发现机器人新趋势
  • Kafka单节点部署
  • 生成式AI和大语言模型 Generative AI LLMs
  • Obsidian 入门使用手册
  • GuLi商城-前端基础Vue指令-单向绑定双向绑定
  • 前端(十三)——JavaScript 闭包的奥秘与高级用法探索
  • 面试-快速学习计算机网络-UDP/TCP
  • 爱校对如何帮助企业和博客主提高在线可见性?
  • MATLAB中xlsread函数用法
  • Prisma.js:JavaScript中的基于代码的ORM
  • 解决问题:在cocos create中如何从b文件调用到a文件里用CC.resource.load动态加载的图集
  • 分布式 - 消息队列Kafka:Kafka 消费者消费位移的提交方式
  • 如何利用 ChatGPT 进行自动数据清理和预处理
  • PHP“牵手”淘宝商品评论数据采集方法,淘宝API接口申请指南
  • 你更喜欢哪一个:VueJS 还是 ReactJS?
  • PyTorch学习笔记(十六)——利用GPU训练
  • 【实战】十一、看板页面及任务组页面开发(三) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十五)
  • 金额千位符自定义指令
  • 请不要用 JSON 作为配置文件,使用JSON做配置文件的缺点