多线程中的单例模式

单线程中的单例模式

在单线程中,实现一个单例模式是简单的:

class Singleton
{
public:static Singleton* get_instance() {if (instance_ == nullptr) {instance_ = new Singleton();}return instance_;}private:Singleton() = default;static Singleton* instance_;
};// 在Singleton头文件中初始化静态成员变量
Singleton* Singleton::instance_ = nullptr;

在这个单例类中, 定义了静态(static)成员变量instance_,这意味着无论创建多少个类的对象,该静态成员都只有一个副本。由于instance_是私有成员,外部无法访问,需要在Singleton所在的头文件中进行初始化Singleton* Singleton::instance_ = nullptr;

在实例化对象时,通过Singleton::get_instance获取实例,根据if (instance_ == nullptr) 判断是否完成了实例化,如果没有,则new一个对象出来。在单线程中,这个过程无需担心数据的竞争问题。

多线程中的单例模式

但是在多线程中,可能出现这样的情况:
thread1和thread2同时执行Singleton::get_instance,这时if (instance_ == nullptr) 均成立,于是两个线程都会执行instance_ = new Singleton();,导致两次分配。两个线程操作的是不同的对象,导致行为上的不一致和不同步。

在多线程中,一种方式是使用双检查实现单例:

class Singleton
{
public:static Singleton* get_instance() {Singleton* tmp = instance_.load(std::memory_order_acquire);if (tmp == nullptr) {std::unique_lock<std::mutex> lk(mutex_);tmp = instance_;if (tmp == nullptr) {tmp = new Singleton();instance_.store(std::memory_order_release);}}return tmp;}private:Singleton() = default;static std::atomic<Singleton*> instance_;static std::mutex mutex_;
};

在这个实现方式中,将单例的成员变量instance_定义为原子变量,并增加了互斥量mutex_。在get_instance()中增加了带锁的双重检查。回过头来重新考虑之前的情况:

thread1和thread2同时执行Singleton::get_instance,这时if (instance_ == nullptr) 均成立,两者分别执行std::unique_lock<std::mutex> lk(mutex_);,假设thread2后执行,则由于先执行的thread1激活了锁,导致thread2被锁阻塞,thread1进入的则进行第二次检查,发现instance_ == nullptr成立,于是进行实例化,并在退出函数后自动释放锁。

随后thread2不再被锁阻塞,重新获取instance_的值,并进行第二次检查,此时instance_ == nullptr不再成立,于是跳过实例化,并返回最新的instance_

原子操作与乱序重排

除了锁的设置,这里还将instance_定义为原子变量,并通过load和store函数对其进行操作,这是为了防止编译优化过程中出现乱序重排。

程序在编译过程中,在不影响程序执行结果的前提下,通过重新编排代码的执行顺序,可以提高代码的执行效率,减少执行时间。

但重排后,在多线程的情况下,可能引发一些问题,比如下面这段代码:

          int x = 0;     // global variableint y = 0;     // global variableThread-1:              Thread-2:
x = 100;               while (y != 200)
y = 200;                   ;std::cout << x;

正常情况下,在threa1中,执行完y=200;后,x的值应该为100,所以thread2的的输出结果为100。但由于threa1中的两条代码在顺序上没有依赖关系,所以在优化过程中,编译器可能将两者的顺序调换,得到如下的顺序:

Thread-1:
y = 200;
x = 100;

在这种情况下,thread2的输出x的结果可能为原先的值,即0。

为防止出现编译优化引发的问题,在前面实现的多线程单例模式中,对instance_操作时分别采用了load()store()。这两者的除了读写instance_外,通过指定参数,还提供了重排的保证:instance_.load(std::memory_order_acquire)用于保证后面访存指令勿重排至此条指令之前instance_.store(std::memory_order_release)用于保证前面访存指令勿重排到此条指令之后。

参考:
atomic原子编程中的Memory Order
理解 C++ 的 Memory Order

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/226171.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【halcon深度学习】目标检测的数据准备过程中的一个库函数determine_dl_model_detection_param

determine_dl_model_detection_param “determine_dl_model_detection_param” 直译为 “确定深度学习模型检测参数”。 这个过程会自动针对给定数据集估算模型的某些高级参数&#xff0c;强烈建议使用这一过程来优化训练和推断性能。 过程签名 determine_dl_model_detection…

Codeforces Round 914 (Div. 2) A~E

A.Forked!&#xff08;思维&#xff09; 题意&#xff1a; 给出骑士的跳跃能力 ( x , y ) (x, y) (x,y) 以及国王和皇后的位置&#xff0c;问有多少个位置可以让骑士可以直接攻击到国王和皇后。 分析&#xff1a; 棋盘非常大 ( 1 0 8 1 0 8 ) (10^{8} \times 10^{8}) (1…

Tomcat-指定启动jdk、修改使用的jdk版本

修改tomcat配置文件setclasspath.sh 配置文件首行增加以下代码&#xff0c;指定启动的jdk&#xff1a; export JAVA_HOME/opt/softwares/jdk1.8.0_211/ export JRE_HOME/opt/softwares/jdk1.8.0_211/jre

力扣labuladong——一刷day75

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣200. 岛屿数量&#xff08;广搜&#xff09;二、力扣200. 岛屿数量&#xff08;深搜&#xff09; 前言 图论&#xff0c;深搜还有广搜都只是手段 一、…

某保险公司持续信赖监控易:优化网络运维,确保业务稳定

一、背景介绍 某保险有限公司&#xff0c;作为一家全国知名的保险公司&#xff0c;始终以科技创新和优质服务为引领&#xff0c;不断优化其业务运营。随着业务的快速发展&#xff0c;其IT系统规模日益庞大&#xff0c;网络运维压力也随之增大。为了提高IT系统的稳定性和可靠性&…

drf入门规范

一 Web应用模式 在开发Web应用中&#xff0c;有两种应用模式&#xff1a; 1.1 前后端不分离 1.2 前后端分离 二 API接口 为了在团队内部形成共识、防止个人习惯差异引起的混乱&#xff0c;我们需要找到一种大家都觉得很好的接口实现规范&#xff0c;而且这种规范能够让后端写…

获取和移除cookie的方法

下载npm的cookie插件, 在utils.js文件中引入插件: 封装原始的Cookies.get()方法: 在xxxx.vue文件中引入方法: 使用getCookie方法获取cookie: 封装 移除cookie: export const removeCookie name>{ const options { path: /, domain: xxx.com }; Cookies.remove(name, opti…

i春秋云镜之Tsclient

首先拿到目标:39.99.137.155 首先通过Fscan扫描目标: 可以看到扫描出来了一个SqlServer的弱口令。 账号是sa&#xff0c;密码是1qaz!QAZ。 这里就不使用navicat进行连接了&#xff0c;这里使用impacket-mssqlclient进行连接。 impacket-mssqlclient sa:1qaz!QAZ39.99.137.…

糖糖别胡说,我真的不是签到题目

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 从前&#xff0c;有 nnn 只萌萌的糖糖&#xff0c;他们分成了两组一起玩游戏。他们会排成一排&#xff0c;第 iii 只糖糖会随机得到一个能力值 bib_ibi​。从第 iii 秒的时候&#xff…

yolov5单目测距+速度测量+目标跟踪

要在YOLOv5中添加测距和测速功能&#xff0c;您需要了解以下两个部分的原理&#xff1a; 单目测距算法 单目测距是使用单个摄像头来估计场景中物体的距离。常见的单目测距算法包括基于视差的方法&#xff08;如立体匹配&#xff09;和基于深度学习的方法&#xff08;如神经网…

数据结构之---- 分治算法

数据结构之---- 分治算法 什么是分治算法&#xff1f; 分治&#xff0c;全称分而治之&#xff0c;是一种非常重要且常见的算法策略。 分治通常基于递归实现&#xff0c;包括 分 和 治 两个步骤&#xff1a; 分&#xff08;划分阶段&#xff09;&#xff1a;递归地将原问题分…

深度解析AI算力:软件测试工程师的新挑战与新机遇

简介&#xff1a;在AI技术日益普及的今天&#xff0c;AI算力以及TOPS这个性能衡量指标越来越引起人们的关注。对于软件测试工程师来说&#xff0c;理解这个概念并了解如何在实践中应用是至关重要的。本文将详细解析AI算力&#xff0c;为何它对软件测试工程师如此重要&#xff0…

冰镇的红酒有什么特色呢?

冰镇红酒远不是一个新现象&#xff0c;大多数葡萄酒饮用者仍然坚持白葡萄酒应该冰镇后饮用&#xff0c;而红葡萄酒应该在室温下饮用这种过时的信念。在几十年或几个世纪的时间里&#xff0c;当“室温”更接近于地窖温度时&#xff0c;这种说法可能是正确的&#xff0c;但在当代…

使用playbook部署k8s集群

1.部署ansible集群 使用python脚本一个简单的搭建ansible集群-CSDN博客 2.ansible命令搭建k8s&#xff1a; 1.主机规划&#xff1a; 节点IP地址操作系统配置server192.168.174.150centos7.92G2核client1192.168.174.151centos7.92G2核client2192.168.174.152centos7.92G2 …

Android 在UploadEventService使用ThreadPoolManager线程管理传递数据给后台

Android 在UploadEventService使用ThreadPoolManager线程管理传递数据给后台&#xff0c;如何实现呢&#xff1f; 可以通过以下步骤使用ThreadPoolManager线程管理传递数据给后台&#xff1a; 创建一个ThreadPoolManager类来管理线程池&#xff0c;比如&#xff1a; public cl…

API测试:从入门到精通,干货满满,趣味无穷

一、引言 随着数字化时代的到来&#xff0c;应用程序接口&#xff08;API&#xff09;已经成为软件开发和交互的核心。API测试是确保API质量和稳定性的关键环节。本文将带你了解API测试的基本概念、方法和技巧&#xff0c;让你在轻松愉快的氛围中掌握这一技能。 二、API测试概…

01-C++基础

c概述 1. 概述 1.1 简介 “c” 中的 来自于 c 语言中的递增运算符&#xff0c;该运算符将变量加 1。 c起初也叫”c with class”。通过名称表明&#xff0c; c是对 c 的扩展&#xff0c;因此 c是 c 语言的超集&#xff0c;这意味着 任何有效的 c 程序都是有效的 c程序。 c程…

《PySpark大数据分析实战》-11.Spark on YARN模式安装Hadoop

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…

我的隐私计算学习——匿踪查询

笔记内容来自多本书籍、学术资料、白皮书及ChatGPT等工具&#xff0c;经由自己阅读后整理而成。 &#xff08;一&#xff09;PIR的介绍 ​ 匿踪查询&#xff0c;即隐私信息检索&#xff08;Private InformationRetrieval&#xff0c;PIR&#xff09;&#xff0c;是安全多方计算…

2012-12-12 下载ndk编译出so和可执行文件,放到android 真机运行,包含源码。

一、下载ndk链接NDK 下载 | Android NDK | Android Developers 二、解压ndk后得到下面的文件&#xff0c;里面包含ndk-build.cmd&#xff0c;这个是用来编译的。 三、Android.mk和C源码。完整源码下载路径https://download.csdn.net/download/qq_37858386/88622702 3.1 A…