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

c++单例模式和call_once函数

单例模式是一种常见的设计模式,用于确保某个类只能创建一个实例。由于单例模式是全局唯一的,因此在多线程中使用单例模式时需要考虑线程安全问题。

1.GetInstance()实例化一个对象
  • 懒汉式:第一次用到类的时候才会去实例化。

懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。

  • 饿汉式:在类的加载的时候就去实例化

饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创

#include<iostream>
#include<thread>
#include<mutex>
#include<string>class Log {
public:Log() {};Log(const Log& log) = delete;Log& operator = (const Log& log) = delete;static Log& GetIntance(){static Log Log;   //饿汉模式,提前声明一个对象,需要的时候直接给。return Log; }/*****************************static Log& GetIntance(){static Log *Log = nullptr;   //懒汉模式,需要的时候再去创建一个对象。if(!Log) Log = new Log;return *Log;}
****************************/};void printfLog(std::string msg){std::cout<<msg<<std::endl;
}void func(std::string msg){Log::GetInstance().printLog("error!!!");
}int main(){std::thread t1(func);std::thread t1(func);t1.json;t2.json;Log::GetInstance().printLog("error!!!");
}

==================================

2.多线程出现线程安全问题

t1和t2同时调用func函数,func函数调用GetInstance函数实例化一个对象,但是如果t1和t2同时new一个对象Log,当new的Log对象的地址还没来得及赋值给Log,t2又new了一个对象,这时就会出现错误。

代码实例如下:

#include<iostream>
#include<thread>
#include<mutex>
#include<string>class Log {
public:Log() {};Log(const Log& log) = delete;Log& operator = (const Log& log) = delete;
/*****************************     static Log& GetIntance(){static Log Log;   //饿汉模式,提前声明一个对象,需要的时候直接给。return Log; }
****************************/static Log& GetIntance(){static Log *Log = nullptr;   //懒汉模式,需要的时候再去创建一个对象。if(!Log) Log = new Log;return *Log;}void printfLog(std::string msg){std::cout<<msg<<std::endl;
}};void func(std::string msg){Log::GetInstance().printLog("error!!!");}int main(){std::thread t1(func);std::thread t1(func);t1.json;t2.json;Log::GetInstance().printLog("error!!!");
}

==================================

从性能线程上的区别:
1、线程安全:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题。

懒汉式本身是非线程安全的。

2、资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内 存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

================================

3.call_once函数

在多线程中,有一种场景是某个任务只需要执行一次,可以用C++11中的std::call_once函数配合std::once_flag来实现。多个线程同时调用某个函数,std::call_once可以保证多个线程对该函数只调用一 次。

   void call_once (once_flag& flag, Fn&& fn, Args&&...args);

        第一个参数是std::once_flag的对象(once_flag是不允许修改的,其拷贝构造函数和operator=函数都声明为delete),第二个参数可调用实体,即要求只执行一次的代码,后面可变参数是其参数列表。

        call_once保证函数fn只被执行一次,如果有多个线程同时执行函数fn调用,则只有一个活动线程(active call)会执行函数,其他的线程在这个线程执行返回之前会处于”passive execution”(被动执行状态)——不会直接返回,直到活动线程对fn调用结束才返回。对于所有调用函数fn的并发线程,数据可见性都是同步的(一致的)。

        如果活动线程在执行fn时抛出异常,则会从处于”passive execution”状态的线程中挑一个线程成为活动线程继续执行fn,依此类推。一旦活动线程返回,所有”passive execution”状态的线程也返回,不会成为活动线程。(实际上once_flag相当于一个锁,使用它的线程都会在上面等待,只有一个线程允许执行。如果该线程抛出异常,那么从等待中的线程中选择一个,重复上面的流程)。
                        

代码实例如下:

#include<iostream>
#include<thread>
#include<mutex>
#include<string>static Log *Log = nullptr;   //懒汉模式,需要的时候再去创建一个对象。
static std::once_flag once;class Log {
public:Log() {};Log(const Log& log) = delete;Log& operator = (const Log& log) = delete;/*****************************static Log& GetIntance(){static Log *Log = nullptr;   //懒汉模式,需要的时候再去创建一个对象。if(!Log) Log = new Log;return *Log;}****************************/     static Log& GetIntance(){std::call_once(once,init);return *Log; }
};static void init()
{if(!Log) Log = new Log;
}void printfLog(std::string msg){std::cout<<msg<<std::endl;
}void func(std::string msg){Log::GetInstance().printLog("error!!!");
}int main(){std::thread t1(func);std::thread t1(func);t1.json;t2.json;Log::GetInstance().printLog("error!!!");
}

相关文章:

  • 一键部署Tesseract-OCR环境C++版本(Windows)
  • 速盾网络:cdn加速技术和云计算的区别
  • spring三种配置方式总结
  • 笔记本上使用usb蓝牙适配器
  • 软件测试相关内容第三弹--软件测试基础
  • round四舍五入在python2与python3版本间区别
  • ubuntu 20.04 安装 huggingface transformers 环境
  • 从零搭建NodeJS项目(小白教程)
  • 2024年阿里云服务器租用费用一年和包月价格表
  • Diddler抓包工具——学习笔记
  • 2. gin中间件注意事项、路由拆分与注册技巧
  • BSD-3-Clause是一种开源软件许可协议
  • 蓝桥杯python常用内置函数
  • 【Web安全】SQL各类注入与绕过
  • 基于神经网络的偏微分方程求解器再度取得突破,北大字节的研究成果入选Nature子刊
  • Docker 笔记(2):Dockerfile
  • Flannel解读
  • MobX
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • webpack4 一点通
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 免费小说阅读小程序
  • 如何设计一个微型分布式架构?
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 跳前端坑前,先看看这个!!
  • 最简单的无缝轮播
  • C# - 为值类型重定义相等性
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ​水经微图Web1.5.0版即将上线
  • (2)(2.10) LTM telemetry
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (6)STL算法之转换
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (一)Dubbo快速入门、介绍、使用
  • (转)3D模板阴影原理
  • (转)甲方乙方——赵民谈找工作
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .NET 读取 JSON格式的数据
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • :=
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?
  • @RequestBody与@ModelAttribute
  • @Resource和@Autowired的区别
  • @我的前任是个极品 微博分析
  • [Android]使用Retrofit进行网络请求
  • [C++]模板与STL简介