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 应用。

109

2025.12.24

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

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

65

2025.12.31

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

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

45

2025.12.31

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

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

40

2025.12.31

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

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

41

2025.12.31

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

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

232

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号