美文网首页
c++ 如何优雅地写线程安全的单例

c++ 如何优雅地写线程安全的单例

作者: lc_fan | 来源:发表于2019-10-14 15:08 被阅读0次

提到线程安全, 大家都知道要加锁要原子化, 但是如何高效的实现呢?
在此介绍几种方法:
1. 直接加锁
2. Double Check Lock (DCL 双重校验加锁)
3. 静态局部变量初始化
4. std::call_once (c++11)

背景

下面看一个工作中实际遇到的case.
根据log发现某处加锁等待了8s多, 严重影响性能. (类名做保密处理)


lock.jpg

点进文件看到代码如下:

// 原始加锁, 等待8s
MySingleton* MySingleton::getInstance() {
    pthread_mutex_lock(&m_mutex);
    if(s_pInstance == NULL){
        s_pInstance = new MySingleton();
    }
    return s_pInstance;
}

改进一: Double Check Lock (DCL 双重校验加锁)

这时候绝大部分人都知道要改成 double check 的方式来避免绝大部分的加锁操作. 毕竟是c++面试几乎必考的知识点.

// 改进一: double check 加锁
MySingleton* MySingleton::getInstance() {
    if(s_pInstance == NULL){ // first check
        pthread_mutex_lock(&m_mutex);
        if(s_pInstance == NULL){ // second check
            s_pInstance = new MySingleton();
        }
    }
    return s_pInstance;
}

然而, 由于编译器会对具体执行步骤优化, 没法保证cpu完全按照我们高级语言顺序执行. 需要加 volatile, atomic, MemoryBarrier() 等操作.
另外也有罕见的, 单例尚未初始化完成, 指针就被赋值返回, 造成使用方崩溃.
总之DCL也有一定的风险和问题.

改进二: c++11 静态局部变量的初始化是线程安全的

c++11 引进了memory model,从此C++11也能识别线程这个概念了. 从而c++11 能够保证静态局部变量的初始化是线程安全的
因此可以改成下面的形式

// 改进二: c++11 静态局部变量的初始化是线程安全的
MySingleton* MySingleton::getInstance() {
    static MySingleton singleton();
    return &singleton;
}

改进三: std::call_once (c++11)

c++11 特性中方法 std::call_once(flag, callable, args) 可以通过不同的flag来保证callable这个函数不会被持有相同flag的来源同时调用执行.

// 改进三: std::call_once (c++11)
[std::once_flag](http://en.cppreference.com/w/cpp/thread/once_flag) g_flag;

MySingleton* MySingleton::getInstance() {
    if(s_pInstance == NULL){
        s_pInstance = std::call_once(g_flag, []()->MySingleton*{return new MySingleton();});
    }
    return s_pInstance;
}

总结

个人认为 std::call_once (c++11) 的方法最为优雅. 不过 静态局部变量初始化比较简洁. 二都ble check就是比较基础的方法.

另外有一篇大神的 博客, 推荐大家看看, 会对c++线程安全单例模式有更深刻的理解.

相关文章

  • c++ 如何优雅地写线程安全的单例

    提到线程安全, 大家都知道要加锁要原子化, 但是如何高效的实现呢?在此介绍几种方法:1. 直接加锁2. Doubl...

  • C++单例模式实现(线程安全&支持多参数构造)

    C++单例模式实现(线程安全&支持多参数构造) 线程安全版本 支持多参数版本的单例类 遇到问题点: std::ca...

  • C++单例模式实现(线程安全&支持多参数构造)

    C++单例模式实现(线程安全&支持多参数构造) 线程安全版本 支持多参数版本的单例类 遇到问题点: std::ca...

  • Java 单例模式

    线程安全的单例模式的几种实现方法分享线程安全的单例模式实现有几种思路,个人认为第2种方案最优雅 饿汉式 借助内部类...

  • 单例

    单例,是Java中很重要的一个设计模式。 实现单例是要考虑并发(线程安全)问题的。 如何实现一个线程安全的单例?你...

  • 单例模式(Singleton)

    一、初始化单例类时即创建单例 饿汉式:(线程安全) 枚举类型:(线程安全) 二、按需、延迟创建单例 懒汉式:(线程...

  • C++设计模式

    单例 单例模式的一种实现(《Effective C++》) 此处是通过C++11新的语义来保证线程的安全性,具体由...

  • 1.5 单例模式

    不做赘述, 单例模式想必大家已经烂熟于心了. 这里提一下多线程如何保证的单例模式的线程安全. 外部的if判断不加锁...

  • 设计模式(2) 单例模式

    单例模式 线程安全的Singleton 会破坏Singleton的情况 线程级Singleton 单例模式是几个创...

  • Controller是单例模式的吗?如何保证线程安全?

    Controller是单例模式的吗?如何保证线程安全? 答:Controller是单例的,也就是说并发请求调用Co...

网友评论

      本文标题:c++ 如何优雅地写线程安全的单例

      本文链接:https://www.haomeiwen.com/subject/otsrmctx.html