0

0

c++怎么实现单例模式_c++线程安全单例写法【进阶】

冰火之心

冰火之心

发布时间:2026-03-16 17:02:08

|

331人浏览过

|

来源于php中文网

原创

最稳妥的线程安全单例写法是 std::call_once 配合局部静态变量,它规避了双重检查锁定的内存序陷阱,利用 C++11 对局部静态变量初始化的线程安全保证,且比手写锁更轻量。

c++怎么实现单例模式_c++线程安全单例写法【进阶】

std::call_once + std::once_flag 是目前最稳妥的线程安全单例写法

直接用 std::call_once 配合局部静态变量,能避开双重检查锁定(DCLP)的手动同步陷阱,也比手写锁更轻量。C++11 起标准保证局部静态变量的初始化是线程安全的,但很多人误以为“只要 static 就自动线程安全”,其实仅限于**首次初始化过程**——后续调用不加锁没问题,但初始化阶段必须靠 std::call_once 或语言机制兜底。

常见错误现象:std::mutex 手动加锁漏释放、pthread_once 混用 C/C++ ABI、或在构造函数里调用虚函数导致未定义行为。

  • 必须把单例对象声明为函数内局部静态变量,不能是类静态成员(否则初始化时机不可控)
  • std::once_flag 必须是静态或全局生命周期,不能是局部栈变量(否则每次调用都新建,失效)
  • 不要在 getInstance() 里做耗时操作(比如文件读取、网络请求),否则会阻塞所有等待线程

为什么 double-checked locking 在 C++ 里容易出错

不是语法错,而是内存序和编译器重排导致的逻辑漏洞。即使加了 std::mutexif (ptr == nullptr) 两层检查,若没用 std::atomic 修饰指针、没加 memory_order_acquire/release,就可能让其他线程看到未构造完成的对象地址。

典型错误现象:程序偶发崩溃在单例对象的某个成员函数第一行,gdb 显示该对象部分字段为零值或垃圾值,但指针非空。

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

  • 必须用 std::atomic<t></t> 存储原始指针,不能用裸指针 + volatile(C++ 中 volatile 不提供线程同步语义)
  • 第一次检查要用 load(std::memory_order_acquire),赋值要用 store(ptr, std::memory_order_release)
  • 构造函数不能抛异常,否则 delete 不会被执行,且 std::atomic 指针不会回滚

懒汉式 vs 饿汉式:选哪个取决于初始化成本和使用确定性

饿汉式(全局静态对象)启动即构造,天然线程安全,但浪费资源;懒汉式按需创建,省资源但引入同步开销。二者没有绝对优劣,只看场景。

即梦AI
即梦AI

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

下载

使用场景举例:日志模块可饿汉(必须早于 main 初始化),配置管理器应懒汉(可能根本不用)。

  • 饿汉式必须确保构造函数不依赖其他未初始化的全局对象(避免静态初始化顺序问题,SIOF)
  • 懒汉式若用局部静态变量实现,C++11 后无需额外同步,是最简方案;若用指针+new,则必须配 std::call_oncestd::atomic
  • Windows DLL 场景下,饿汉式可能触发 DLL_THREAD_ATTACH 失败,懒汉式更可控

std::shared_ptr 能不能用来实现线程安全单例

可以,但不推荐。用 std::shared_ptr 管理单例生命周期看似方便,实则引入原子引用计数开销,且无法阻止用户意外拷贝出多个 std::shared_ptr 实例,破坏“单例”语义。

常见错误现象:代码里出现两个不同地址的“同一个单例”,表现为状态不一致、资源重复释放。

  • 若坚持用 std::shared_ptr,必须把构造函数设为私有,并禁用拷贝,只允许通过 getInstance() 返回 const 引用或 std::shared_ptr 的 const 版本
  • std::shared_ptr 的线程安全性仅限于“多个线程同时读/写不同实例”,对同一实例的 operator=reset() 仍需外部同步
  • 真正需要共享所有权的场景,往往已不该叫“单例”,而应叫“全局服务对象”,此时考虑依赖注入更合适

最易被忽略的一点:单例的析构时机。C++ 标准不保证静态对象析构顺序,若单例析构时访问了已被销毁的其他静态对象,就会 crash。别依赖“反正程序快结束了”,尤其在测试框架或插件系统里,这个问题会反复出现。

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

847

2023.08.22

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

565

2023.09.20

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

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

335

2025.08.29

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

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

108

2025.10.23

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

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

77

2025.10.23

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

448

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

606

2023.08.10

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

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

786

2023.08.10

chatgpt官网入口地址合集
chatgpt官网入口地址合集

本专题整合了chatgpt官网入口地址、使用教程等内容,阅读专题下面的文章了解更多详细内容。

0

2026.03.16

热门下载

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

精品课程

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

共94课时 | 11.5万人学习

C 教程
C 教程

共75课时 | 5.5万人学习

C++教程
C++教程

共115课时 | 22.2万人学习

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

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