多线程安全单例模式的传统解决方案与现代方法

news2024/12/26 17:50:45

在多线程环境中实现安全的单例模式时,传统的双重检查锁(Double-Checked Locking)方案和新型的std::once_flagstd::call_once机制是两种常见的实现方法。它们在实现机制、安全性和性能上有所不同。

1. 传统的双重检查锁方案

双重检查锁(Double-Checked Locking)是一种在多线程环境中实现线程安全的单例模式的常见技术。其基本思想是在获取锁之前进行一次检查,以减少不必要的锁争用。

示例代码
#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton* getInstance() {
        if (instance == nullptr) { // 第一次检查
            std::lock_guard<std::mutex> lock(mtx); // 获取锁
            if (instance == nullptr) { // 第二次检查
                instance = new Singleton();
            }
        }
        return instance;
    }

private:
    Singleton() { /* 构造函数 */ }
    static Singleton* instance;
    static std::mutex mtx;
};

Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();
    std::cout << "Same instance: " << (s1 == s2) << std::endl;
    return 0;
}

问题分析

虽然双重检查锁在大多数情况下是有效的,但它存在以下问题:

  1. 编译器优化问题:编译器可能会对代码进行优化,导致instance = new Singleton()的执行顺序发生变化,从而引发潜在的未定义行为。
  2. 内存模型问题:在C++11之前的标准中,线程之间的内存可见性没有明确规定,因此即使使用双重检查锁,也可能出现多个线程同时创建实例的情况。

2. 新型的std::once_flagstd::call_once机制

C++11引入了std::once_flagstd::call_once,提供了一种更简洁、更安全的实现线程安全单例模式的方法。std::call_once确保指定的函数只被调用一次,即使多个线程同时调用。

示例代码
#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        std::call_once(initFlag, initSingleton);
        return *instance;
    }

private:
    Singleton() { /* 构造函数 */ }
    static Singleton* instance;
    static std::once_flag initFlag;

    static void initSingleton() {
        instance = new Singleton();
    }
};

Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::initFlag;

int main() {
    Singleton& s1 = Singleton::getInstance();
    Singleton& s2 = Singleton::getInstance();
    std::cout << "Same instance: " << (&s1 == &s2) << std::endl;
    return 0;
}

优点
  1. 安全性std::call_once由标准库提供,确保了线程安全性和内存模型的正确性,消除了双重检查锁方案中的编译器优化和内存模型问题。
  2. 简洁性:代码更简洁,不需要手动处理锁和双重检查逻辑。
  3. 性能:在多次调用getInstance时,std::call_once避免了不必要的锁争用,性能更好。

总结

传统的双重检查锁方案虽然在大多数情况下是有效的,但它存在编译器优化和内存模型问题。相比之下,std::once_flagstd::call_once机制提供了更安全、更简洁、性能更好的实现方式,是实现线程安全单例模式的首选方法。

使用std::call_once不仅可以避免复杂的锁机制和双重检查逻辑,还能确保线程安全性和内存模型的正确性,是现代C++中推荐的多线程编程技术。

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

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

相关文章

机器学习8-决策树CART原理与GBDT原理

Gini 系数 和Gini 系数增益 CART决策树算法流程举例 该篇文章对于CART的算法举例讲解&#xff0c;一看就懂。 决策树(Decision Tree)—CART算法 同时也可以观看视频 分类树 GBDT原理举例 可以看如下示例可以理解GBDT的计算原理 用通俗易懂的方式讲解&#xff1a; GBDT算法及…

oracle中删除指定前缀的表

近期接手做的项目&#xff0c;发觉数据库中有许多多余的表。究其原因&#xff0c;应该是同事贪图方便&#xff0c;将过去做过的项目复制粘贴&#xff0c;然后修修改改。包括数据库也是克隆过来的&#xff0c;然后又没有删除本项目多余的表&#xff0c;结果经过几个轮回&#xf…

JAVA篇10 —— 常用类WrapperStringMathArraysSystemBigIntegerBigDecimal日期

欢迎来到我的主页&#xff1a;【一只认真写代码的程序猿】 本篇文章收录于专栏【小小爪哇】 如果这篇文章对你有帮助&#xff0c;希望点赞收藏加关注啦~ 目录 1 包装类 1.1 包装类和String 1.2 int&char包装类常用方法 2 String类 3 Math 类 4 Arrays类 5 System类…

tauri使用github action打包编译多个平台arm架构和inter架构包踩坑记录

这些error的坑&#xff0c;肯定是很多人不想看到的&#xff0c;我的开源软件PakePlus是使用tauri开发的&#xff0c;PakePlus是一个界面化将任何网站打包为轻量级跨平台软件的程序&#xff0c;利用Tauri轻松构建轻量级多端桌面应用和多端手机应用&#xff0c;为了实现发布的时候…

通义灵码走进北京大学创新课堂丨阿里云云原生 10 月产品月报

云原生月度动态 云原生是企业数字创新的最短路径。 《阿里云云原生每月动态》&#xff0c;从趋势热点、产品新功能、服务客户、开源与开发者动态等方面&#xff0c;为企业提供数字化的路径与指南。 趋势热点 &#x1f947; 通义灵码走进北京大学创新课堂&#xff0c;与 400…

鸿蒙开发-HMS Kit能力集(地图服务、华为支付服务)

地图服务 Map Kit&#xff08;地图服务&#xff09;是鸿蒙生态下的一个地图服务&#xff0c;为开发者提供强大而便捷的地图能力&#xff0c;助力全球开发者实现个性化地图呈现、地图搜索和路线规划等功能&#xff0c;轻松完成地图构建工作。 Map Kit提供了千万级别的 Poi&…

【四轴】基于IIC通信读写MPU6050寄存器

1. 基本原理 在这篇【四轴】软件IIC通信的实现 – Dukis Blog博客中&#xff0c;我介绍了软件IIC的实现方式。而MPU6050&#xff0c;正是一种通过IIC进行通信的传感器外设。 1.1 什么是MPU6050 MPU6050 是 InvenSense 公司推出的一款6 轴惯性传感器模块&#xff0c;广泛应用于姿…

arkTS:使用ArkUI实现用户信息的持久化管理与自动填充(PersistentStorage)

arkUI&#xff1a;使用ArkUI实现用户信息的持久化管理与自动填充&#xff08;PersistentStorage&#xff09; 1 主要内容说明2 例子2.1 登录页2.1.1登陆页的相关说明2.1.1.1 持久化存储的初始化2.1.1.2 输入框2.1.1.3 记住密码选项2.1.1.4 登录按钮的逻辑2.1.1.5 注册跳转 2.1.…

基于SpringBoot+Vue的美妆购物网站

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

【SpringBoot+Vue】x-admin管理系统跟做

技术栈 前端技术说明Vue前端框架Vuex全局状态管理框架ElementUI前端UI框架Axios前端HTTP框架vue-element-admin项目脚手架 后端技术说明SpringBoot容器MVC框架MyBatisORM框架MyBatis-plusMyBatis增强工具Redis非关系型数据库 数据库准备 SET NAMES utf8mb4; SET FOREIGN_KE…

【Docker】Docker配置远程访问

配置Docker的远程访问&#xff0c;你需要按照以下步骤进行操作&#xff1a; 1. 在Docker宿主机上配置Docker守护进程监听TCP端口 Docker守护进程默认只监听UNIX套接字&#xff0c;要实现远程访问&#xff0c;需要修改配置以监听TCP端口。 ‌方法一&#xff1a;修改Docker服务…

LeetCode hot100(自用背诵、部分题目、非最优解)

点击题目可以跳转到LeetCode 哈希 两数之和 public int[] twoSum(int[] nums, int target) {int lengthnums.length;int[] ans new int[2];for (int i 0; i <length-1 ; i) {for (int j i1; j < length; j) {if(nums[i]nums[j]target){ans[0]i;ans[1]j;}}}return an…

Android -- 简易音乐播放器

Android – 简易音乐播放器 播放器功能&#xff1a;* 1. 播放模式&#xff1a;单曲、列表循环、列表随机&#xff1b;* 2. 后台播放&#xff08;单例模式&#xff09;&#xff1b;* 3. 多位置同步状态回调&#xff1b;处理模块&#xff1a;* 1. 提取文件信息&#xff1a;音频文…

基础Web安全|SQL注入

基础Web安全 URI Uniform Resource Identifier&#xff0c;统一资源标识符&#xff0c;用来唯一的标识一个资源。 URL Uniform Resource Locator&#xff0c;统一资源定位器&#xff0c;一种具体的URI&#xff0c;可以标识一个资源&#xff0c;并且指明了如何定位这个资源…

ESG研究报告白皮书与ESG治理报告合集(2020-2023年)

一.资料范围&#xff1a;&#xff08;1&#xff09;ESG白皮书及指南;&#xff08;2&#xff09;ESG研究报告,&#xff08;3&#xff09;ESG治理报告分析&#xff08;4&#xff09;上市公司ESG报告&#xff08;知名企业&#xff09; 二、资料用途&#xff1a;可以分析研究企业E…

WPF指示灯的实现方式

WPF指示灯的实现方式 样式 XAML <Button x:Name"Btn1" Width"25" Height"25" Grid.Row"0" Grid.Column"1" Margin"10 5 5 5 "><Button.Template><ControlTemplate TargetType"Button"…

初识QT第一天

思维导图 利用Qt尝试做出原神登陆界面 import sys from PyQt6.QtGui import QIcon, QPixmap, QMovie from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QLineEdit# 封装原神窗口类 class Genshin(QWidget):# 构造函数def __init__(self):# 初始化父类…

【Linux】线程池设计 + 策略模式

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; 线程池 1-1 ⽇志与策略模式1-2 线程池设计1-3 线程安全的单例模式1-3-1 什么是单例模式1-3-2 单例模式的特点1-3-3 饿汉实现⽅式和懒汉实现⽅式1-3-4 饿汉…

vim插件管理器vim-plug替代vim-bundle

文章目录 vim-plug与vim-bundle对比vim-plug安装vim-plug管理安装插件相关文章 vim-plug与vim-bundle对比 vim-plug 和 vim-bundle 都是 Vim 的插件管理器&#xff0c;但它们有一些关键的区别。以下是两者的主要对比&#xff1a; 易用性和简洁性 vim-plug: 易用性: vim-plug …

107.【C语言】数据结构之二叉树求总节点和第K层节点的个数

目录 1.求二叉树总的节点的个数 1.容易想到的方法 代码 缺陷 思考:能否在TreeSize函数内定义静态变量解决size的问题呢? 其他写法 运行结果 2.最好的方法:分而治之 代码 运行结果 2.求二叉树第K层节点的个数 错误代码 运行结果 修正 运行结果 其他写法 1.求二…