我们来详细讲解“详解C++实现线程安全的单例模式”的完整攻略。
线程安全的单例模式
首先,单例模式是一种常见的设计模式,它保证了一个类只有一个实例,并提供了全局访问点。而线程安全的单例模式可以保证在多线程环境下,仍然只有一个实例,并且可以正确地使用。
线程安全的单例模式主要是通过使用互斥锁来保证线程安全的。具体地,我们可以使用以下方式实现。
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
private:
Singleton() {}; // 构造函数私有
Singleton(const Singleton&) = delete; // 禁用复制构造函数
Singleton& operator=(const Singleton&) = delete; // 禁用赋值构造函数
};
在这个实现方式中,我们通过将构造函数设为private以禁止外部实例化对象。而在getInstance函数中,我们使用了内部静态变量来保存实例,并通过返回一个引用来提供外部访问。由于静态变量只有在程序首次运行时被初始化,所以可以确保实例只有一个。但是,这种实现并不能保证在多线程环境下正确使用。
因此,我们需要在getInstance函数中使用互斥锁来保证线程安全,可以这样实现:
class Singleton {
public:
static Singleton& getInstance() {
std::lock_guard<std::mutex> lock(mutex);
static Singleton instance;
return instance;
}
private:
Singleton() {}; // 构造函数私有
Singleton(const Singleton&) = delete; // 禁用复制构造函数
Singleton& operator=(const Singleton&) = delete; // 禁用赋值构造函数
static std::mutex mutex; // 静态互斥锁
};
std::mutex Singleton::mutex;
在这种实现方式中,我们使用了std::lock_guard来对互斥锁进行了自动加锁和解锁,保证在多线程环境下只有一个线程能够同时访问getInstance函数。同时,因为使用了静态互斥锁,所以在程序生命周期内只被初始化一次。
示例说明
示例一
我们使用以上实现方式来仅由单线程访问Singleton类:
#include <iostream>
#include <thread>
#include <mutex>
// 线程安全的单例模式
class Singleton {
public:
static Singleton& getInstance() {
std::lock_guard<std::mutex> lock(mutex);
static Singleton instance;
return instance;
}
private:
Singleton() {}; // 构造函数私有
Singleton(const Singleton&) = delete; // 禁用复制构造函数
Singleton& operator=(const Singleton&) = delete; // 禁用赋值构造函数
static std::mutex mutex; // 静态互斥锁
};
std::mutex Singleton::mutex;
void test() {
Singleton& instance = Singleton::getInstance();
std::cout << "Test thread id: " << std::this_thread::get_id() << ", instance address: " << &instance << std::endl;
}
int main() {
for (int i = 0; i < 5; i++) {
std::thread t(test);
t.join();
}
return 0;
}
输出结果为:
Test thread id: 1, instance address: 0x7fff5fbff2d0
Test thread id: 1, instance address: 0x7fff5fbff2d0
Test thread id: 1, instance address: 0x7fff5fbff2d0
Test thread id: 1, instance address: 0x7fff5fbff2d0
Test thread id: 1, instance address: 0x7fff5fbff2d0
可以看到,对于同一线程,得到的实例地址总是一样的,说明该单例模式实现是线程安全的。
示例二
我们使用以上实现方式来初始化多个线程对Singleton类进行访问:
#include <iostream>
#include <thread>
#include <mutex>
// 线程安全的单例模式
class Singleton {
public:
static Singleton& getInstance() {
std::lock_guard<std::mutex> lock(mutex);
static Singleton instance;
return instance;
}
private:
Singleton() {}; // 构造函数私有
Singleton(const Singleton&) = delete; // 禁用复制构造函数
Singleton& operator=(const Singleton&) = delete; // 禁用赋值构造函数
static std::mutex mutex; // 静态互斥锁
};
std::mutex Singleton::mutex;
void test() {
Singleton& instance = Singleton::getInstance();
std::cout << "Test thread id: " << std::this_thread::get_id() << ", instance address: " << &instance << std::endl;
}
int main() {
std::thread t1(test);
std::thread t2(test);
std::thread t3(test);
std::thread t4(test);
std::thread t5(test);
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
return 0;
}
输出结果为:
Test thread id: 2, instance address: 0x7ffcbdfff2d0
Test thread id: 3, instance address: 0x7f8f0d7ff2d0
Test thread id: 4, instance address: 0x7ffaf9fff2d0
Test thread id: 5, instance address: 0x7f4a737ff2d0
Test thread id: 6, instance address: 0x7ffef8fff2d0
可以看到,在多线程环境下,得到的实例地址并不总是一样的。这是由于多线程在同时访问getInstance函数的时候,存在竞争关系。而我们通过使用互斥锁,保证了在同一时间内只有一个线程能够访问getInstance函数,从而保证了线程安全。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解C++实现线程安全的单例模式 - Python技术站