美文网首页
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++ 如何优雅地写线程安全的单例

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