单例模式的使用
1.单例模式:what :
C++中,单例模式是一种设计模式,确保一个类仅有一个实例存在整个程序的生命周期中,并且为这个实例提供一个全局访问点。比如大型软件系统中,有一个配置类,只需要创建一个实例就可以为整个系统提供服务。如果创建多个实例,不仅会浪费系统资源,还可能导致配置信息不一致。通过单例模式,我们可以保证配置类在整个程序中只有一个实例,所有需要获取或修改系统配置的地方都可以通过这个单例来操作。
1. 可以节省内存资源;2. 方便管理全局资源。3.避免资源重复占用和冲突。多线程通过共享实例。
2.单例模式:How:
实现的具体的关键点:
首先将构造函数私有化。这是为了防止外部代码通过new操作符随意创建类的实例,从而确保整个实例只有一个实例存在。
class Singleton{
private:
Singleton(){};
};
其次,需要有一个私有静态实例指针来保证保存唯一的实例。因为是静态的整个程序运行期间都存在,并且只有一份。
class Singleton{
private:
Singleton(){}
static Singleton* instance;//私有静态实例指针。
};
Singleton* Singleton:: instance = nullptr;//类外初始化指针为nullptr;
最后,提供一个公共静态方法作为获取静态实例的接口。
class Singleton{
private:
Singleton() {}
static Singleton* instance;
public:
static Singleton* getInstace(){
if(instance == nullptr){
instance = new Singleton();
}
return instance;
}
static Singleton* Singleton:: instance = nullptr;
2.1懒汉式
刚刚我们实现的就叫做懒汉式,为什么叫懒汉式呢,因为是在getInstance中,才回去创建实例,在一些情况下可以去节省资源。不过多线程不安全。为什么呢:比如线程A和线程B都进入了判定实例不为空的代码体中,这就会造成多个实例。
为什么叫“懒汉式”
-
懒汉式(Lazy Initialization):只有当调用
getInstance的时候,才真正去构造对象。 -
优点:如果程序运行过程中压根没用到
Singleton,就不会浪费资源去构造对象。 -
缺点:多线程环境下,第一次构造时存在线程安全问题。
加锁实现:不让多个线程进入代码体就行。
class Singleton{
private:
Singleton() {}
static Singleton* instance;
static std::mutex mtx;
public:
static Singleton* getInstace(){
std::lock_guard<std::mutex> lock(mtx);
if(instance == nullptr){
instance = new Singleton();
}
return instance;
}
static Singleton* Singleton:: instance = nullptr;
static Singleton::mutex;
互斥锁:std::lock_gurad<std::mutex> lock(mtx);RAII风格:为了解决懒汉模式的线程安全问题,可以使用互斥锁。这里使用了std::lock_guard,它是一个 RAII(Resource Acquisition Is Initialization)类,在构造时自动加锁,在析构时自动解锁。当一个线程进入getInstance方法时,std::lock_guard会自动加锁,这样其他线程就无法进入这个代码块,直到当前线程解锁。在加锁后,再判断instance是否为空,如果为空则创建实例,从而保证了在多线程环境下只有一个实例被创建。
虽然加锁实现保证了线程安全,但也带来了性能开销。每次调用getInstance方法时都需要加锁和解锁,而加锁和解锁操作是有一定时间成本的。如果这个方法被频繁调用,那么频繁的加锁解锁操作会降低程序的执行效率。就像每次进入一个房间都要进行繁琐的安检(加锁)和解除安检(解锁),会大大影响进入房间的速度。在高并发场景下,这种性能开销可能会成为系统的瓶颈。
#include <mutex>
class Singleton {
private:
Singleton() {} // 私有构造函数
~Singleton() {} // 私有析构函数
Singleton(const Singleton&) = delete; // 禁止拷贝
Singleton& operator=(const Singleton&) = delete; // 禁止赋值
static Singleton* instance; // 指针实例
static std::mutex mtx; // 互斥锁
public:
static Singleton* getInstance() {
if (instance == nullptr) { // 第一次检查
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) { // 第二次检查
instance = new Singleton();
}
}
return instance;
}
};
// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
-
双重检查锁(DCLP)
-
第一次
if (instance == nullptr):大多数情况下,实例已经创建好了,就不用加锁,提高性能。 -
第二次
if (instance == nullptr):在锁住的情况下再判断一次,避免两个线程同时通过第一次检查,导致创建多个实例。
-
-
C++11 内存模型保证
-
C++11 对静态局部变量的初始化做了线程安全保证。
-
所以更简洁的写法是使用 静态局部变量法:
-
class Singleton {
private:
Singleton() {}
~Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
static Singleton instance; // C++11 保证线程安全初始化
return instance;
}
};
2.2饿汉式
class Singleton {
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton instance; // 静态对象,类加载时就创建
public:
static Singleton& getInstance() {
return instance;
}
};
// 定义
Singleton Singleton::instance;
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
#include <chrono>
// ============== 1) 饿汉式(Eager Singleton)==================
// 程序启动(类加载)即创建实例:简单、天然线程安全;但可能从未使用也占资源。
class EagerSingleton {
private:
EagerSingleton() { std::cout << "[Eager] ctor\n"; }
~EagerSingleton() = default;
EagerSingleton(const EagerSingleton&) = delete;
EagerSingleton& operator=(const EagerSingleton&) = delete;
static EagerSingleton instance; // 静态对象(推荐优于 new 指针)
public:
static EagerSingleton& getInstance() { return instance; }
void doWork() const { std::cout << "[Eager] this=" << this << "\n"; }
};
EagerSingleton EagerSingleton::instance; // 定义 + 初始化
// ============== 2) Meyers 懒汉式(C++11+,最推荐)=============
// 第一次调用 getInstance() 才创建;C++11 起,静态局部变量初始化是线程安全的。
class MeyersSingleton {
private:
MeyersSingleton() { std::cout << "[Meyers] ctor\n"; }
~MeyersSingleton() = default;
MeyersSingleton(const MeyersSingleton&) = delete;
MeyersSingleton& operator=(const MeyersSingleton&) = delete;
public:
static MeyersSingleton& getInstance() {
static MeyersSingleton inst; // 线程安全的一次性初始化
return inst;
}
void doWork() const { std::cout << "[Meyers] this=" << this << "\n"; }
};
// ============== 3) call_once 懒汉式(可控生命周期)=============
// 和 Meyers 一样只初始化一次,但可以在需要时 destroy() 主动释放资源。
class OnceSingleton {
private:
OnceSingleton() { std::cout << "[Once] ctor\n"; }
~OnceSingleton() { std::cout << "[Once] dtor\n"; }
OnceSingleton(const OnceSingleton&) = delete;
OnceSingleton& operator=(const OnceSingleton&) = delete;
static std::once_flag once_;
static OnceSingleton* ptr_;
static void init() { ptr_ = new OnceSingleton(); }
public:
static OnceSingleton& getInstance() {
std::call_once(once_, init);
return *ptr_;
}
static void destroy() { delete ptr_; ptr_ = nullptr; } // 需要时机可控的资源释放
void doWork() const { std::cout << "[Once] this=" << this << "\n"; }
};
std::once_flag OnceSingleton::once_;
OnceSingleton* OnceSingleton::ptr_ = nullptr;
// ============== 并发压力测试辅助函数 ============================
template<typename Fn>
void run_threads(const char* title, Fn f, int n_threads = 8) {
std::cout << "\n=== " << title << "(并发演示) ===\n";
std::vector<std::thread> ths;
ths.reserve(n_threads);
for (int i = 0; i < n_threads; ++i) {
ths.emplace_back([f, i]{
// 模拟竞争(随机微小延迟)
std::this_thread::sleep_for(std::chrono::milliseconds(1 + (i % 3)));
f();
});
}
for (auto& t : ths) t.join();
}
int main() {
// 1) 饿汉式:程序一开始就已创建(可观察到最先打印 ctor)
run_threads("EagerSingleton", []{
EagerSingleton::getInstance().doWork();
});
// 2) Meyers 懒汉式:首次调用时才创建(只会出现一次 ctor)
run_threads("MeyersSingleton", []{
MeyersSingleton::getInstance().doWork();
});
// 3) call_once 懒汉式:首次调用时创建;还可主动 destroy()
run_threads("OnceSingleton (first wave)", []{
OnceSingleton::getInstance().doWork();
});
// 如果想演示销毁再创建(谨慎:真实业务一般避免重复构造/销毁)
std::cout << "\n--- destroy OnceSingleton and call again ---\n";
OnceSingleton::destroy();
run_threads("OnceSingleton (second wave)", []{
OnceSingleton::getInstance().doWork();
});
// 程序结束:Eager/Meyers 的静态对象由运行时自动析构;
// OnceSingleton 我们手动释放过一次,第二次不释放也会由 OS 回收内存(演示目的)。
return 0;
}
局部静态变量(function-local static)的初始化是按需的(第一次进入时才初始化),并且必须是线程安全的。
更多推荐
所有评论(0)