单例模式(Singleton Pattern)是一种设计模式,旨在确保一个类只有一个实例,并提供全局访问点。
单例模式的优点
- 控制实例化 :
单例模式确保某个类只有一个实例,这对于需要集中管理、全局共享资源的场景非常有用。例如,数据库连接、线程池、日志管理器等资源的共享。 - 全局访问 :
单例模式提供了全局访问点,任何地方都能通过该点访问到该对象的唯一实例。这使得在不同的类和模块中能够共享同一个对象,而无需传递实例引用。 - 确保一致性 :
由于只有一个实例,单例模式能够确保状态的一致性。在多个线程或多个请求中,所有对该实例的修改都会反映到同一个实例中,避免了数据不一致的问题。
适用场景
单例模式适合用在以下几种情况:
- 系统中需要频繁使用某个对象,且该对象的创建代价较高。
- 需要全局访问某个对象,而不希望它被多次创建。
- 系统中有共享的资源需要进行集中管理(例如配置管理、线程池等)。
示例
- CMakeLists.txt
bash
cmake_minimum_required(VERSION 3.20)
project(singleton_test)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
file(GLOB SOURCES
"main.cpp"
"singleton.h"
"random_number_generator.h"
"random_number_generator.cpp")
add_executable(singleton_test ${SOURCES})
- main.cpp
cpp
#include <iostream>
#include "random_number_generator.h"
using singleton::randomNum;
int main()
{
randomNum().start();
return 0;
}
- random_number_generator.h
cpp
#ifndef RANDOM_NUMBER_GENERATOR_H
#define RANDOM_NUMBER_GENERATOR_H
#include <random>
#include "singleton.h"
namespace singleton {
class RandomNumberGenerator
{
public:
// 启动方法,开始每隔 3 秒生成一个随机数
void start();
private:
// 构造函数
RandomNumberGenerator();
// 随机数生成器和分布
std::default_random_engine generator; // 随机数生成器
std::uniform_int_distribution<int> distribution; // 随机数分布
friend struct DefaultSingletonTraits<RandomNumberGenerator>;
};
/// 获取单例
inline RandomNumberGenerator &randomNum()
{
return Singleton<RandomNumberGenerator>::instance();
}
} // namespace singleton
#endif // RANDOM_NUMBER_GENERATOR_H
- random_number_generator.cpp
为了确保在Linux系统中整个应用程序只能运行一个实例,并且强制实现单例模式的全局唯一性,使用命名锁(例如文件锁或 POSIX 锁)来确保只有一个进程可以创建和访问单例实例。
使用flock
(文件锁)来确保在 Linux 系统上同一时间只有一个进程能够访问RandomNumberGenerator
的单例实例。
1.创建一个锁文件,通过flock
来保证文件的独占访问。
2.锁文件只有在进程成功获得锁时才能继续执行,并创建唯一的RandomNumberGenerator
实例。
3.退出时释放文件锁,确保其他进程可以访问文件。
cpp
#include "random_number_generator.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <fcntl.h> // open
#include <sys/file.h> // flock
#include <unistd.h> // close
namespace singleton {
// 构造函数初始化随机数生成器和分布
RandomNumberGenerator::RandomNumberGenerator() : generator(std::random_device{}()), distribution(1, 100)
{
}
// 文件锁路径,用于确保程序只能运行一个实例
const char *LOCK_FILE_PATH = "/tmp/random_number_generator.lock";
// start() 方法:每隔 3 秒生成一个随机数
void RandomNumberGenerator::start()
{
// 打开锁文件
int lock_file = open(LOCK_FILE_PATH, O_CREAT | O_WRONLY, 0666);
if (lock_file == -1) {
std::cerr << "Error opening lock file." << std::endl;
return;
}
// 尝试获取文件锁
if (flock(lock_file, LOCK_EX | LOCK_NB) == -1) {
std::cerr << "Another instance is already running." << std::endl;
close(lock_file);
return;
}
std::cout << "Lock acquired. Starting random number generation..." << std::endl;
// 当获得锁后开始生成随机数
while (true) {
int random_number = distribution(generator);
std::cout << "Random number: " << random_number << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
}
// 释放锁并关闭文件
flock(lock_file, LOCK_UN); // 释放锁
close(lock_file); // 关闭文件
}
} // namespace singleton
- singleton.h
通过Singleton
类实现了一个线程安全的单例模式,采用了自旋锁(原子操作)来确保在多线程环境中只有一个线程能够创建单例实例 ,另外通过std::shared_ptr
自动管理内存。DefaultSingletonTraits
提供了默认的内存管理机制,用户可以根据需要自定义内存分配和销毁策略。
cpp
#ifndef SINGLETON_H
#define SINGLETON_H
#include <mutex>
#include <atomic>
#include <memory>
namespace singleton {
// 为 Singleton 类提供默认的内存分配和销毁机制
// Default traits for Singleton<Type>. Calls operator new and operator delete on the object. Registers automatic deletion at process exit.
// Overload if you need arguments or another memory allocation function.
template <typename Type>
struct DefaultSingletonTraits
{
// Allocates the object.
static Type *New()
{
// The parenthesis is very important here; it forces POD type initialization.
return new Type();
}
// Destroys the object.
static void Delete(Type *x) { delete x; }
};
template <typename T, typename Traits = DefaultSingletonTraits<T>>
class Singleton
{
public:
// 返回单例实例,如果实例尚未创建,则会创建它。为了保证线程安全,使用了自旋锁机制(通过 atomic_exchange_explicit 和 atomic_store_explicit)来确保在多线程环境中只有一个线程能够创建单例实例。
static T &instance()
{
if (!instance_) {
// In the rare event that two threads come into this section only one will acquire the spinlock and build the actual instance
while (std::atomic_exchange_explicit(&lock_, true, std::memory_order_acquire)) {
; // spin until acquired
}
if (!instance_) {
// This is the thread that will build the real instance since
// it hasn-t been instantiated yet
instance_ = std::shared_ptr<T>(Traits::New(), [](T *p) { Traits::Delete(p); });
valid_ = true;
}
// Release spinlock
std::atomic_store_explicit(&lock_, false, std::memory_order_release);
}
return *instance_;
}
static bool isValid() { return valid_; }
static void destroy()
{
if (instance_) {
while (std::atomic_exchange_explicit(&lock_, true, std::memory_order_acquire)) {
; // spin until acquired
}
instance_.reset();
valid_ = false;
// Release spinlock
std::atomic_store_explicit(&lock_, false, std::memory_order_release);
}
}
private:
Singleton();
static std::atomic<bool> lock_;
static std::atomic<bool> valid_;
static std::shared_ptr<T> instance_; // 单例对象的持有者
};
template <typename T, typename Traits>
std::shared_ptr<T> Singleton<T, Traits>::instance_ = nullptr;
template <typename T, typename Traits>
std::atomic<bool> Singleton<T, Traits>::lock_;
template <typename T, typename Traits>
std::atomic<bool> Singleton<T, Traits>::valid_;
} // namespace singleton
#endif // SINGLETON_H