0

0

C++单例模式如何避免双重检查锁定问题 现代C++11原子变量实现方案

P粉602998670

P粉602998670

发布时间:2025-07-09 11:30:02

|

231人浏览过

|

来源于php中文网

原创

双重检查锁定的问题在于可能因编译器或cpu重排序导致未完全初始化的对象被访问,引发未定义行为。解决方案包括:1. 使用std::atomic和内存顺序控制实现线程安全的单例;2. 采用局部静态变量方式由编译器自动处理同步问题;3. 注意指针管理时的析构清理和不同平台的测试验证。

C++单例模式如何避免双重检查锁定问题 现代C++11原子变量实现方案

在C++中实现单例模式时,双重检查锁定(Double-Checked Locking)虽然能减少锁的开销,但如果不小心处理,很容易引入竞态条件和内存可见性问题。特别是在多线程环境下,使用普通的双重检查锁定并不安全。幸运的是,现代C++11之后的标准提供了原子变量和内存顺序控制,可以更安全地解决这个问题。

C++单例模式如何避免双重检查锁定问题 现代C++11原子变量实现方案

什么是双重检查锁定的问题?

传统的双重检查锁定写法是这样的:

C++单例模式如何避免双重检查锁定问题 现代C++11原子变量实现方案
if (instance == nullptr) {
    lock();
    if (instance == nullptr) {
        instance = new Singleton();
    }
    unlock();
}

这个逻辑看起来合理,但在实际执行中,由于编译器优化或CPU乱序执行,new Singleton()可能会被重排为先分配内存地址再构造对象。如果另一个线程在此时读取到未完全初始化的instance,就会导致访问一个无效的对象,引发未定义行为。

立即学习C++免费学习笔记(深入)”;

使用C++11原子变量实现安全的单例

从C++11开始,我们可以借助std::atomic和适当的内存顺序来避免这些问题。下面是基于原子变量的一种实现方式:

英特尔AI工具
英特尔AI工具

英特尔AI与机器学习解决方案

下载
C++单例模式如何避免双重检查锁定问题 现代C++11原子变量实现方案
class Singleton {
public:
    static Singleton& getInstance() {
        // 第一次检查
        Singleton* tmp = instance.load(std::memory_order_acquire);
        if (!tmp) {
            std::lock_guard lock(mutex_);
            tmp = instance.load(std::memory_order_relaxed);
            if (!tmp) {
                tmp = new Singleton();
                instance.store(tmp, std::memory_order_release);
            }
        }
        return *tmp;
    }

private:
    static std::atomic instance;
    static std::mutex mutex_;
    Singleton() {}
};

这种方式的关键点在于:

  • 使用 std::atomic 来保证指针读写的原子性和可见性。
  • 在第一次无锁读取时使用 memory_order_acquire,确保后续对对象的访问不会被提前。
  • 存储新实例时使用 memory_order_release,确保对象构造完成后再对外暴露。
  • 内部锁只在初始化阶段使用,后续访问无需加锁,性能较好。

更简洁的方式:利用局部静态变量

如果你不需要懒加载或者希望代码更简洁,C++11还提供了一个更推荐的做法——局部静态变量:

static Singleton& getInstance() {
    static Singleton instance;
    return instance;
}

这个写法在C++11标准中是线程安全的,并且由编译器自动处理初始化的同步问题,是最简单也最推荐的方式之一。

当然,这种写法无法手动控制释放时机,适用于程序运行期间一直需要存在的单例对象。

小细节需要注意的地方

  • 如果你确实要用指针方式管理生命周期,记得在析构函数中做清理,或者添加自定义销毁方法。
  • 使用std::atomic时,不同平台的实现可能有差异,务必测试并发环境下的行为。
  • 对于性能敏感场景,可以尝试用memory_order_consume进一步优化,但要非常小心,因为它的语义较复杂。

基本上就这些。现代C++已经提供了足够强大的工具来安全地实现单例,只要注意内存顺序和初始化时机,就能避免传统双重检查锁定带来的问题。

相关专题

更多
c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

95

2025.10.23

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

472

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

107

2025.12.24

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

php网站源码教程大全
php网站源码教程大全

本专题整合了php网站源码相关教程,阅读专题下面的文章了解更多详细内容。

4

2025.12.31

视频文件格式
视频文件格式

本专题整合了视频文件格式相关内容,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

不受国内限制的浏览器大全
不受国内限制的浏览器大全

想找真正自由、无限制的上网体验?本合集精选2025年最开放、隐私强、访问无阻的浏览器App,涵盖Tor、Brave、Via、X浏览器、Mullvad等高自由度工具。支持自定义搜索引擎、广告拦截、隐身模式及全球网站无障碍访问,部分更具备防追踪、去谷歌化、双内核切换等高级功能。无论日常浏览、隐私保护还是突破地域限制,总有一款适合你!

7

2025.12.31

出现404解决方法大全
出现404解决方法大全

本专题整合了404错误解决方法大全,阅读专题下面的文章了解更多详细内容。

42

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
c语言项目php解释器源码分析探索
c语言项目php解释器源码分析探索

共7课时 | 0.3万人学习

nginx浅谈
nginx浅谈

共15课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号