懒汉单例模式出现的线程安全问题(C++)

在懒汉模式中,由于存在一个并没有被实例话的静态指针,在多线程环境中,导致在静态获取实例方法/函数时,会有多个线程进入到该代码进行实例对象(线程安全问题),下面展示存在线程安全的懒汉单例模式代码:

Singleton.h

class Singleton {
public:
    static Singleton* instance();
    void show();
private:
    static Singleton* singleton;
    static int num;
};

Singleton.cpp

#include "Singleton.h"

Singleton* Singleton::singleton = nullptr;
int Singleton::num = 0;

Singleton* Singleton::instance(){
    //this_thread::sleep_for(chrono::seconds(1));
    if(singleton == nullptr){
        //位置1
        singleton = new Singleton();
        num++;
    }
    return singleton;
}

void Singleton::show(){
    cout << "num : " << num << endl;
}

在上述Singleton.cpp文件中的 “位置1” 处,当有多个线程同时判断singleton为nullptr时,,每个线程都会在堆空间申请一个实例对象,因此这里线程获取到的实例对象不一致。

解决上述问题一种方法就是加锁。下述给出加锁的线程安全instance代码:

#include "Singleton.h"

Singleton* Singleton::singleton = nullptr;
int Singleton::num = 0;
mutex Singleton::m;

Singleton* Singleton::instance(){
    //this_thread::sleep_for(chrono::seconds(1));
    m.lock();
        if(singleton == nullptr){
            singleton = new Singleton();
            num++;
        }
    m.unlock();
    return singleton;
}

void Singleton::show(){
    cout << "num : " << num << endl;
}

在上述代码中,在判断实例对象是否是nullptr时,进行加锁,一个线程进行加锁之后,其他线程不能访问后面的资源转而等待锁,当锁释放时,其他线程之间又进行资源竞争。这样便保证了只有一个实例存在。But,这样存在一个又存在严重的问题,每次获取实例时,都要进行加锁,造成严重的性能问题。如何避免这个问题?我们使用双层对象判断。下面给出代码及解释。

#include "Singleton.h"

Singleton* Singleton::singleton = nullptr;
int Singleton::num = 0;
mutex Singleton::m;

Singleton* Singleton::instance(){
    //this_thread::sleep_for(chrono::seconds(1));
    if(singleton == nullptr){
        m.lock();
        if(singleton == nullptr) {
            singleton = new Singleton();
            num++;
        }
        m.unlock();
    }
    return singleton;
}

void Singleton::show(){
    cout << "num : " << num << endl;
}

上述的代码首先,判断singleton是否为nullptr,如果是,在多线程环境下,其中一个线程获得了资源进行加锁,进行实例对象,其他线程可能会被锁在第一层singleton==nullptr的下面进行等待,获得锁之后singleton已经不为nullptr了,则不会进行初始化,这样便保证了只有一个实例的情况下,还能获取良好的性能。

综上所述,只停留在理论的层面,下面进行多线程的实验。

首先我们建立线程回调函数,显示num的数量。

void show_instance_info(){
    Singleton::instance()->show();
}

在使用C++线程创建5个线程。

void test_singleton(){
    thread t1(show_instance_info);
    thread t2(show_instance_info);
    thread t3(show_instance_info);
    thread t4(show_instance_info);
    thread t5(show_instance_info);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
}

 

1.测试执行线程不安全的代码结果:

可以看到多次执行会有不同的结果,有多个对象进行了实例化。

1.测试执行线程安全的代码结果:

 

 

不管试验多少次,num总是1。 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

顾文繁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值