懒汉单例模式出现的线程安全问题(C++)
Posted 顾文繁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了懒汉单例模式出现的线程安全问题(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。
以上是关于懒汉单例模式出现的线程安全问题(C++)的主要内容,如果未能解决你的问题,请参考以下文章