0

0

C++中的双重检查锁定(Double-Checked Locking)是什么?(如何实现单例)

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-02-23 15:55:23

|

505人浏览过

|

来源于php中文网

原创

std::call_once比手写双重检查更安全,因其由标准库保证仅执行一次、原子性及自动内存屏障,避免重排序、裸指针竞态、volatile误用及异常安全等问题。

c++中的双重检查锁定(double-checked locking)是什么?(如何实现单例)

为什么直接用 std::call_once 比手写双重检查更安全

双重检查锁定(DCLP)在 C++11 之前是单例线程安全初始化的“权宜之计”,但容易因内存重排序、编译器优化或缺少内存屏障而崩溃——比如构造函数还没执行完,另一个线程就拿到了未初始化的 instance 指针并开始调用成员函数。

现代 C++(C++11 起)提供了更简单、无歧义的替代方案:std::call_once + std::once_flag。它由标准库保证:仅执行一次、原子性、自动插入必要内存屏障,且不依赖指针状态判断。

  • std::call_once 是语言级保障,无需手动管理锁、判断、释放等逻辑
  • 避免了 volatile 的误用(它在 C++ 中对 DCLP 完全无效)
  • 不涉及原始指针生命周期管理,规避 new 失败后未清理、析构时机不确定等问题

手写双重检查锁定的典型错误写法

下面这段代码看似合理,实则危险:

if (instance == nullptr) {
    std::lock_guard<std::mutex> lock(mutex_);
    if (instance == nullptr) {
        instance = new Singleton(); // ❌ 构造可能被重排到赋值之后
    }
}

问题不止在构造顺序:C++ 标准不保证 new 分配内存 → 调用构造函数 → 写入 instance 这三步的可见性顺序。其他线程可能看到非空的 instance,但其内部成员仍是垃圾值。

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

  • 漏掉 std::atomic<singleton></singleton> 声明,导致读写非原子,触发未定义行为
  • volatile Singleton* 替代原子操作,完全无效(volatile 不提供同步语义)
  • 没用 memory_order_acquire/release,即使用了 std::atomic,默认 memory_order_seq_cst 也常被误简化

如果真要手写 DCLP,必须满足哪些条件

仅当目标平台/编译器明确支持、且你已充分理解内存模型时才考虑。否则请用 std::call_once

MyMap AI
MyMap AI

使用AI将想法转化为图表

下载
  • instance 必须声明为 std::atomic<singleton></singleton>,不能是裸指针或 volatile
  • 第一次读取用 .load(std::memory_order_acquire),第二次写入用 .store(ptr, std::memory_order_release)
  • 构造必须在临界区内完成,且确保异常安全(例如用 std::unique_ptr 管理中间对象)
  • 所有访问 instance 的地方都需用原子读取(不能直接解引用裸指针)

示例关键片段:

static std::atomic<Singleton*> instance{nullptr};
// ...
Singleton* ptr = instance.load(std::memory_order_acquire);
if (ptr == nullptr) {
    std::lock_guard<std::mutex> lock(mutex_);
    ptr = instance.load(std::memory_order_relaxed);
    if (ptr == nullptr) {
        ptr = new Singleton(); // 构造完成后才 store
        instance.store(ptr, std::memory_order_release);
    }
}

静态局部变量方案其实更推荐

C++11 规定:函数内静态局部变量的初始化是线程安全的,且仅发生一次。它底层通常就用 std::call_once 实现,但语法干净、无出错空间。

  • 无需显式锁、原子变量、内存序,也不用担心 delete 或生命周期
  • 初始化失败(抛异常)会自动重试下次调用,符合预期
  • 比手写 DCLP 更快(无原子读开销),比 std::call_once 更少样板代码

写法就是:

static Singleton& getInstance() {
    static Singleton instance; // ✅ 线程安全、懒加载、自动销毁
    return instance;
}

注意:这个 instance 是对象本身,不是指针;若需指针,返回 &instance 即可。

真正难的是让所有人相信——最简单的写法,恰恰是最正确、最可维护、最容易通过 Code Review 的写法。别在单例上赌自己比标准库更懂并发。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

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

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

294

2025.08.29

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

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

105

2025.10.23

c++中volatile关键字的作用
c++中volatile关键字的作用

本专题整合了c++中volatile关键字的相关内容,阅读专题下面的文章了解更多详细内容。

71

2025.10.23

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

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

715

2023.08.10

数据库Delete用法
数据库Delete用法

数据库Delete用法:1、删除单条记录;2、删除多条记录;3、删除所有记录;4、删除特定条件的记录。更多关于数据库Delete的内容,大家可以访问下面的文章。

287

2023.11.13

drop和delete的区别
drop和delete的区别

drop和delete的区别:1、功能与用途;2、操作对象;3、可逆性;4、空间释放;5、执行速度与效率;6、与其他命令的交互;7、影响的持久性;8、语法和执行;9、触发器与约束;10、事务处理。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

221

2023.12.29

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

1044

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

334

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

213

2026.02.13

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 10万人学习

C 教程
C 教程

共75课时 | 4.9万人学习

C++教程
C++教程

共115课时 | 19.1万人学习

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

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