首页 > 后端开发 > C++ > 正文

c++如何实现一个线程安全的单例_c++ Meyers' Singleton与std::call_once【多线程】

尼克
发布: 2025-12-07 23:00:07
原创
932人浏览过
最推荐使用Meyers’ Singleton(局部静态变量),因其由C++11标准保证线程安全、懒加载、自动销毁;需传参或延迟初始化时用std::call_once;应避免手写双重检查锁定(DCLP)。

c++如何实现一个线程安全的单例_c++ meyers\' singleton与std::call_once【多线程】

在C++多线程环境下,实现线程安全的单例最推荐的方式是使用Meyers’ Singleton(即局部静态变量版本),配合C++11起标准保证的初始化线程安全性;而std::call_once则适合更复杂、需延迟初始化或带参数构造的场景。两者都无需手动加锁,避免了双重检查锁定(DCLP)的经典陷阱。

Meyers’ Singleton:最简、最安全的写法

C++11标准明确规定:函数内局部静态变量的首次初始化是线程安全的——编译器会自动插入必要的同步机制(如pthread_once或类似逻辑),且仅发生一次。

示例:

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance; // ✅ 线程安全!C++11保证
        return instance;
    }
<pre class="brush:php;toolbar:false;">Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
登录后复制

private: Singleton() = default; // 可含初始化逻辑(如读配置、建连接) };

说明:

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

  • 无需std::mutex,无竞态,无内存序烦恼
  • 懒加载(第一次调用getInstance()时才构造)
  • 自动销毁(程序退出时按逆序析构,线程安全)
  • 注意:构造函数内不要调用getInstance(),否则可能死锁或未定义行为

std::call_once + std::once_flag:需要显式控制初始化时机时用

当单例构造逻辑不能放在默认构造函数中(比如需传参、依赖外部对象、或想分离“声明”和“初始化”),可用std::call_once配合std::once_flag确保只执行一次。

万彩商图
万彩商图

专为电商打造的AI商拍工具,快速生成多样化的高质量商品图和模特图,助力商家节省成本,解决素材生产难、产图速度慢、场地设备拍摄等问题。

万彩商图 212
查看详情 万彩商图

示例:

class ConfigurableSingleton {
public:
    static ConfigurableSingleton& getInstance(int port) {
        std::call_once(init_flag, [port]() {
            instance.reset(new ConfigurableSingleton(port));
        });
        return *instance;
    }
<p>private:
ConfigurableSingleton(int p) : port_(p) { /<em> ... </em>/ }
static std::unique_ptr<ConfigurableSingleton> instance;
static std::once_flag init<em>flag;
int port</em>;
};</p><p>std::unique_ptr<ConfigurableSingleton> ConfigurableSingleton::instance;
std::once_flag ConfigurableSingleton::init_flag;
登录后复制

说明:

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

  • std::call_once保证lambda最多执行一次,即使多个线程同时进入也安全
  • 适合构造开销大、或需运行时参数的场景
  • 需自行管理生命周期(如用std::unique_ptr),析构不自动(可加atexit或静态对象辅助)
  • 注意:不要在lambda里再调用getInstance,防止递归+死锁

为什么不用双重检查锁定(DCLP)?

经典DCLP写法(用volatile + 手动new + 双重if + mutex)在C++11前易出错,主要原因:

  • volatile不提供内存序保证,编译器/CPU重排可能导致返回未完全构造的对象指针
  • 需搭配std::atomic_thread_fence等才能正确,代码复杂且易错
  • Meyers’ 和 std::call_once已由标准保证,更简洁可靠

小结:选哪个?

✅ 默认首选 Meyers’ Singleton:简单、安全、自管理、零成本抽象
✅ 需要参数/条件初始化 → 用 std::call_once
❌ 避免手写DCLP,除非你明确需要绕过静态初始化(极少见)

基本上就这些 —— C++11之后,单例的线程安全已经不复杂,但容易忽略标准提供的保障。

以上就是c++++如何实现一个线程安全的单例_c++ Meyers' Singleton与std::call_once【多线程】的详细内容,更多请关注php中文网其它相关文章!

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

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

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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