目录
[一. 线程池](#一. 线程池)
[1. 解决了那些问题](#1. 解决了那些问题)
[1.1 降低资源消耗(解决创建/销毁线程的高成本问题)](#1.1 降低资源消耗(解决创建/销毁线程的高成本问题))
[1.2 控制并发线程数量,避免资源耗尽](#1.2 控制并发线程数量,避免资源耗尽)
[1.3 提高任务响应速度](#1.3 提高任务响应速度)
[1.4 统一管理和监控线程资源](#1.4 统一管理和监控线程资源)
[1.5 避免线程泄漏](#1.5 避免线程泄漏)
[2. 实现思路](#2. 实现思路)
[2.3. 核心流程实现](#2.3. 核心流程实现)
[2.4. 关键技术点](#2.4. 关键技术点)
[二. 单例模式](#二. 单例模式)
[1. 什么是设计模式](#1. 什么是设计模式)
[2. 什么是单例模式](#2. 什么是单例模式)
[1. 饿汉模式(Eager Initialization)](#1. 饿汉模式(Eager Initialization))
[2. 懒汉模式(Lazy Initialization,饱汉模式)](#2. 懒汉模式(Lazy Initialization,饱汉模式))
[(2)线程安全版(C++11 及以上推荐)](#(2)线程安全版(C++11 及以上推荐))
一. 线程池
1. 解决了那些问题
1.1 降低资源消耗(解决创建/销毁线程的高成本问题)
线程的创建会消耗CPU资源(分配创建新的TCB,分配独立的栈空间,继承主线程的页表和文件描述,添加至调度队列,寄存器中设置独立上下文),线程的销毁亦是。所以频繁的创建和销毁会大幅度降低CPU的性能。
而线程池避免了频繁的创建和销毁,创建一定数量的线程进行复用,尤其适合短期任务频繁执行的场景
TCB分配与初始化 :这不仅仅是分配一小块内存。
task_struct是一个极其庞大的结构体(在最新内核中可能有几KB大小),初始化其数十个字段本身就是不小的计算任务。栈空间分配 :虽然分配虚拟地址空间本身很快,但物理内存的分配是"按需"通过页故障实现的。新线程开始运行时,会触发一系列的缺页中断,这会导致内核陷入、分配物理页、更新页表,成本很高。
调度器开销:
队列操作 :将线程加入调度队列需要锁来保护队列的一致性,这在多核环境下可能引发锁竞争。
缓存失效:当一个新线程被调度到CPU上时,它几乎没有任何数据在CPU的高速缓存中(即"冷缓存")。这会导致它开始运行的初期性能很差,直到慢慢将所需数据加载到缓存中。同时,它还可能"挤掉"其他线程的热数据,导致系统整体缓存效率下降。
1.2 控制并发线程数量,避免资源耗尽
CPU资源耗尽
-
过度的上下文切换:大量可运行线程会导致操作系统花费大量时间在线程切换上,而不是执行实际任务。这会显著降低吞吐量,CPU时间都被浪费在"管理"线程上了。
-
**缓存抖动:**频繁切换线程,会导致CPU的各级缓存(L1/L2/L3)频繁失效,每个线程都需要加载自己的资源,导致缓存命中率急剧下降。
内存资源耗尽
-
每个线程都需要分配独立的栈空间(默认从几KB到几MB不等)。1000个线程就可能占用数GB的虚拟内存,并消耗大量物理内存作为后备存储。
-
最终导致内存严重不足,OS杀死进程(触发linux的OOM Killer),或者进行频繁切换线程,导致OS近乎停滞
其他资源的耗尽
-
文件描述符:每个线程可能打开文件、网络连接,耗尽系统允许的最大文件描述符数量。
-
**网络带宽/连接数:**过多并发的发送网络请求,会导致自身或对方无法承受
-
**数据库连接:**耗尽连接池,导致无法连接或数据库响应缓慢
1.3 提高任务响应速度
线程池中的线程处于就绪态,当有新任务的时候,无需等待线程创建,而是直接分配线程去执行,减少任务启动延迟。
1.4 统一管理和监控线程资源
线程池提供了对线程的集中管理能力:
- 控制任务队列长度(当线程都在忙碌时,任务可暂存于队列,避免任务丢失)。
- 支持线程池参数的动态调整(如核心线程数,最大线程数)。
- 监控任务完成情况(如完成数,失败数,执行时间等),便于问题排斥和性能提升。
1.5 避免线程泄漏
若手动管理线程时未及时回收(如任务执行异常结束,导致线程未释放),可能造成的内存泄漏,长期如此会导致系统资源耗尽,线程池通过内部机制保证线程的生命周期管理。
2. 实现思路
线程池的主要作用是为了**管理任务队列,复用线程,控制并发量,**其思路可以拆分为以下几个关键步骤和组件:
2.1.核心组件定义:
- 线程池管理器:负责线程池的创建、销毁、参数配置(如核心线程数、最大线程数等)。
- 工作线程(Worker):预先创建线程进行线程等待,循环从任务队列中提取任务并执行。
- **任务队列(Task Queue):**存错待执行的任务(通常是阻塞队列,避免线程空轮询)。
- **任务接口(Runnable/Callable):**定义任务的执行逻辑,线程池执行并接受任务的接口。
2.2.核心参数设计
线程池的行为由一些参数控制,需要初始化时定义:
- 核心线程数(corePoolSize):线程池长期维持的最小线程数(即使空闲也不销毁)。
- **最大线程数(maximunPoolSize):**线程池中最多可存在线程数(核心线程忙时,可临时扩大至此)。
- 空闲线程可存活时间(KeepAliveTime):超过核心线程数的临时现场,空闲时存在最长时间,过后销毁。
- 任务队列(WorkQueue):用于缓存任务的阻塞队列。
- 拒绝策略(RejectedExecutionHandler):当任务队列满且线程数达到最大值时,处理新任务的策略(如丢弃,抛出异常,让提交者执行等)
2.3. 核心流程实现
(1)初始化线程池
- 启动时根据
corePoolSize创建核心线程,这些线程进入阻塞等待状态 (通过阻塞队列的take()方法等待任务)。 - 初始化任务队列和拒绝策略。
(2)提交任务的处理逻辑
当外部提交任务时,线程池按以下逻辑处理:
- 若当前线程数 < 核心线程数:直接创建新线程执行任务(即使有其他核心线程空闲,也会优先新建到核心数)。
- 若当前线程数 ≥ 核心线程数:将任务加入任务队列,等待空闲线程从队列中获取执行。
- 若任务队列已满 :
- 若当前线程数 < 最大线程数:创建临时线程执行任务(临时线程空闲后会按
keepAliveTime销毁)。 - 若当前线程数 ≥ 最大线程数:触发拒绝策略处理新任务。
- 若当前线程数 < 最大线程数:创建临时线程执行任务(临时线程空闲后会按
(3)工作线程的运行逻辑
每个工作线程(Worker)是一个循环体,逻辑如下:
// 伪代码示意
while (true) {
Task task = workQueue.take(); // 阻塞等待队列中的任务
try {
task.run(); // 执行任务
} catch (Exception e) {
// 处理任务执行异常
}
}
- 核心线程:即使任务队列空,也会一直阻塞等待新任务(不会被销毁)。
- 临时线程:若空闲时间超过
keepAliveTime,则退出循环并销毁。
(4)线程池的销毁
- 调用销毁方法(如
shutdown())时,线程池不再接收新任务,等待已提交的任务执行完毕后,中断所有工作线程,释放资源。
2.4. 关键技术点
- 阻塞队列 :使用阻塞队列(如
BlockingQueue)实现任务的缓存和线程间的同步,避免线程空轮询(通过take()阻塞等待)。 - 线程复用:工作线程通过循环从队列取任务,避免了频繁创建 / 销毁线程的开销。
- 并发控制 :通过锁(如
ReentrantLock)或原子操作保证线程池状态(如运行、关闭)和参数修改的线程安全。 - 动态调整:支持运行时调整核心线程数、最大线程数等参数(需配合锁保证线程安全)。
- 代码示例(简易)
cond.hpp
#pragma once
#include "Mutex.hpp"
namespace CondModule
{
class Cond
{
public:
Cond()
{
std::cout << " Cond()" << std::endl;
pthread_cond_init(&_cond, NULL);
}
~Cond()
{
std::cout << " ~Cond()" << std::endl;
pthread_cond_destroy(&_cond);
}
void Wait(MutexModule::Mutex& mutex)
{
std::cout << " Wait(MutexModule::Mutex& mutex)" << std::endl;
pthread_cond_wait(&_cond, mutex.GetMutex());
}
void Singel()
{
pthread_cond_signal(&_cond);
}
void Broadcast()
{
pthread_cond_broadcast(&_cond);
}
private:
pthread_cond_t _cond;
};
};
Mutex.hpp
#pragma once
#include <pthread.h>
namespace MutexModule
{
// using namespace LogModule;
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&_mutex, NULL);
}
~Mutex()
{
pthread_mutex_destroy(&_mutex);
}
void Lock()
{
int n = pthread_mutex_lock(&_mutex);
if(n != 0)
{
return;
}
}
void UnLock()
{
int n = pthread_mutex_unlock(&_mutex);
if(n != 0)
{
return;
}
}
pthread_mutex_t* GetMutex()
{
return &_mutex;
}
private:
pthread_mutex_t _mutex;
};
class LockGuard
{
public:
LockGuard(Mutex& mutex)
:_mutex(mutex)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.UnLock();
}
private:
Mutex& _mutex;
};
}
pthread.hpp
#include <pthread.h>
#include <string>
#include <functional>
#include <atomic>
#include "Log.hpp"
namespace PthreadModule
{
namespace{
static std::atomic<int> number{0};//原子计数器
}
class Thread
{
using fun_t = std::function<void()>;
private:
void Enabled_Detach()
{
_isDetach = true;
}
void Enabled_Run()
{
_isRun = true;
}
static void* Routine(void* args)// 类中存在this指针,void*(*)(void*) 签名不匹配
{
Thread* self = static_cast<Thread*>(args);
self->Enabled_Run();
self->Enabled_Detach();
self->Detach();
pthread_setname_np(self->_tid, self->_name.c_str());
self->_fun();
return (void*)0;
}
public:
Thread(fun_t fun)
:_tid(0),
_isDetach(false),
_isRun(false),
_fun(fun)
{
_name = "pthread-" + std::to_string(number++);
}
bool start()
{
if(_isRun)
return false;
int ret = pthread_create(&_tid, NULL, Routine, this);
if(ret == 0)
{
LogModule::LOG(LogModule::LogLevel::INFO) << "create success!";
return true;
}
else
{
LogModule::LOG(LogModule::LogLevel::ERROR) << "create success!";
return false;
}
}
void stop()
{
if(_isRun)
{
int ret = pthread_cancel(_tid);
LogModule::LOG(LogModule::LogLevel::INFO) << "pthread Has been cancelled";
_isRun = false;
(void)ret;
}
else
{
LogModule::LOG(LogModule::LogLevel::INFO) << "pthread Not in operation";
}
}
void Detach() {
if(_isDetach && _isRun)
{
// LogModule::LOG(LogModule::LogLevel::INFO) << "已调用";
int ret = pthread_detach(_tid);
(void)ret;
}
}
void Jion()
{
if(_isDetach) return;
int ret =pthread_join(_tid, NULL);
(void)ret;
}
const std::string Name()
{
return _name;
}
~Thread(){}
private:
pthread_t _tid;
std::string _name;
bool _isDetach;
bool _isRun;
fun_t _fun;
};
}
以上代码可直接替换为C++类部实现方式
log.hpp
#pragma once
#include <iostream>
#include <filesystem>
#include <string>
#include <memory>
#include <sstream>
#include <unistd.h>
#include <ctime>
#include <fstream>
#include "Mutex.hpp"
namespace LogModule
{
using namespace std;
// 日志等级
enum class LogLevel
{
DEBUG = 1,
INFO,
WARNING,
ERROR,
FATAL,
};
// 获取日志等级字符串
string LevelToString(LogLevel level)
{
switch(level)
{
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO";
case LogLevel::WARNING: return "WARNING";
case LogLevel::ERROR: return "ERROR";
case LogLevel::FATAL: return "FATAL";
default: return "UNKNOWN";
}
}
// 获取时间字符串
string GetCurrentTime()
{
time_t curTime = time(nullptr);
struct tm currTm;
char timeStr[128] = {0};
localtime_r(&curTime, &currTm);
snprintf(timeStr, sizeof(timeStr), "%04d-%02d-%02d %02d:%02d:%02d",
currTm.tm_year + 1900,
currTm.tm_mon + 1,
currTm.tm_mday,
currTm.tm_hour,
currTm.tm_min,
currTm.tm_sec);
return timeStr;
}
// 日志策略基类
class LogStrategy
{
public:
virtual ~LogStrategy() = default;
virtual void Log(const string& message) = 0; //纯虚函数
};
// 终端打印策略
class TerminalPrintStrategy : public LogStrategy
{
public:
void Log(const string& message) override
{
MutexModule::LockGuard lock(_mutex);
cout << message << endl;
}
private:
MutexModule::Mutex _mutex;
};
class FilePrintStrategy : public LogStrategy
{
public:
FilePrintStrategy()
:_path("./log"),
_file("log.log")
{
MutexModule::LockGuard lock(_mutex);
if(filesystem::exists(_path))
{
return;
}
try
{
filesystem::create_directory(_path);
}
catch(const filesystem::filesystem_error &err)
{
cout << err.what() << endl;
}
}
~FilePrintStrategy(){}
void Log(const string& message) override
{
MutexModule::LockGuard lock(_mutex);
string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
ofstream out(filename, ios::app);//以追加方式打开
if(!out.is_open()) return;
out << message << "\r\n";
out.close();
}
private:
string _path;
string _file;
MutexModule::Mutex _mutex;
};
// 日志器
class Logger
{
public:
Logger()
{
EnableTerminalOutput();
}
void EnableTerminalOutput()
{
_strategy = make_unique<TerminalPrintStrategy>();
}
void EnableFileOutput()
{
_strategy = make_unique<FilePrintStrategy>();
}
class LogMessage
{
public:
LogMessage(LogLevel level, const string& srcName, int line, Logger& logger)
: _level(level),
_logger(logger)
{
stringstream header;
header << "\033[1;32m[" << GetCurrentTime() << "]"
<< "[" << srcName << ":" << line << ":" << getpid() << ":"
<< LevelToString(level) << "]\033[0m ";
_logMessage = header.str();
}
template <typename T>
LogMessage& operator<<(const T& value)
{
stringstream ss;
ss << value;
_logMessage += ss.str();
return *this;
}
~LogMessage()
{
if(_logger._strategy)
{
_logger._strategy->Log(_logMessage);
}
}
private:
LogLevel _level;
string _logMessage;
Logger& _logger;
};
LogMessage operator()(LogLevel level, const string& srcName, int line)
{
return LogMessage(level, srcName, line, *this);
}
private:
unique_ptr<LogStrategy> _strategy;
};
// 全局日志器实例
Logger logger;
// 日志宏
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Terminal logger.EnableTerminalOutput()
#define Enable_File logger.EnableFileOutput()
}
pthreadPool.hpp
#include <vector>
#include <queue>
#include "Pthread.hpp"
#include "Cond.hpp"
namespace ThreadPoolModule
{
using namespace PthreadModule;
using namespace MutexModule;
using namespace CondModule;
using namespace LogModule;
static const int _default = 2;
template <typename T>
class ThreadPool
{
private:
void wakeUpOne()
{
_cond.Singel();
}
void wakeUpAll()
{
LockGuard lock(_mutex);
if(_IsSleepNum)
{
_cond.Broadcast();
}
_IsSleepNum = 0;
}
ThreadPool(int num = _default)
:_num(num),
_IsSleepNum(0),
_Isrun(false)
{
for(int i = 0; i < _num; i++)
{
_threads.emplace_back([this](){
HandlerTask();
});//容器中构造元素(避免临时对象拷贝)
}
}
~ThreadPool(){}
void start()
{
if(_Isrun) return;
_Isrun = true;
for(auto& th : _threads)
{
bool ret = th.start();
LOG(LogModule::LogLevel::INFO) << th.Name();
(void)ret;
}
}
public:
void stop() {
if (!_Isrun) return;
{
LockGuard lock(_mutex);
_Isrun = false;
_cond.Broadcast(); // 唤醒所有线程
}
for (auto& th : _threads) {
th.stop(); // 确保调用 Thread 的停止逻辑
}
}
void HandlerTask()
{
char pthreadName[64] = {0};
pthread_getname_np(pthread_self(), pthreadName, sizeof(pthreadName));
T t;
while(1)
{
{
LockGuard lock(_mutex);
while(_Isrun && _tasks.empty())//使用while检测,防止线程虚假唤醒
{
// LOG(LogModule::LogLevel::INFO) << pthreadName << "无任务"<<_IsSleepNum;
++_IsSleepNum;
LOG(LogModule::LogLevel::INFO) << pthreadName << "等待任务"<<_IsSleepNum;
_cond.Wait(_mutex);
LOG(LogModule::LogLevel::INFO) << pthreadName << "有无任务";
--_IsSleepNum;
}
if(!_Isrun && _tasks.empty())
{
LOG(LogModule::LogLevel::INFO) << pthreadName << " quit...";
break;
}
t = _tasks.front();
_tasks.pop();
}
t();//执行任务不需要加锁,并非临界资源
}
}
bool QueueAddTask(const T& in)
{
if(_Isrun)
{
// LOG(LogModule::LogLevel::INFO) << "添加任务";
_tasks.push(in);
if(_IsSleepNum == _tasks.size())
{
wakeUpOne();
}
return true;
}
return false;
}
static ThreadPool<T> *GetInstance()
{
if(inc == nullptr)
{
// LOG(LogModule::LogLevel::INFO) << "inc == nullptr";
LockGuard lock(mutex);
if(inc == nullptr)
{
inc = new ThreadPool<T>();
inc->start();
}
}
// LOG(LogModule::LogLevel::INFO) << "inc != nullptr";
return inc;
}
private:
std::vector<Thread> _threads;
std::queue<T> _tasks;
Mutex _mutex;
Cond _cond;
int _num; // 线程池总数
int _IsSleepNum;
bool _Isrun;
static ThreadPool<T>* inc;
static Mutex mutex;
};
template <typename T>
ThreadPool<T>* ThreadPool<T>::inc = nullptr;
template <typename T>
Mutex ThreadPool<T>::mutex;
}
mian.cpp
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "Task.hpp"
#include <random>
#include <mutex>
thread_local std::mt19937 gen(std::random_device{}());
void Result() {
std::uniform_int_distribution<int> dist_x(0, 9);
std::uniform_int_distribution<int> dist_y(0, 19);
int x = dist_x(gen);
int y = dist_y(gen);
LogModule::LOG(LogModule::LogLevel::INFO) << x + y;
// std::cout << x + y << std::endl;
}
// #define offsetof(type, member) ((size_t)&(((type *)0)->member))
int main()
{
while(1)
{
ThreadPoolModule::ThreadPool<task_t>::GetInstance()->QueueAddTask(Result);
sleep(1);
}
ThreadPoolModule::ThreadPool<task_t>::GetInstance()->stop();
return 0;
}
二. 单例模式
1. 什么是设计模式
设计模式是在软件开发中,针对特定问题场景总结出的可复用解决方案,是经过验证的、优化的代码设计经验。它描述了在特定场景下,如何组织类、对象、接口及其交互关系,以解决代码复用、扩展性、可读性等问题。
设计模式不直接提供具体代码,而是提供一种思想或模板,帮助开发者写出更健壮、灵活、易于维护的代码。常见的设计模式分类包括创建型(如单例、工厂)、结构型(如代理、装饰器)、行为型(如观察者、策略)等。
2. 什么是单例模式
单例模式是创建型设计模式 的一种,其核心目标是:保证一个类在整个应用中只能实例化出一个对象,并提供一个全局唯一的访问点。
单例模式通常用于控制资源的统一管理(如线程池、数据库连接池、日志工具等),避免重复创建对象导致的资源浪费或状态不一致问题。
1. 饿汉模式(Eager Initialization)
核心思想:在类加载(静态成员初始化)时就创建唯一实例,依赖 C++ 全局 / 静态变量的初始化机制保证单例。
cpp
运行
#include <iostream>
class SingletonHungry {
private:
// 1. 私有静态成员:类加载时初始化唯一实例
static SingletonHungry instance;
// 2. 私有构造函数:禁止外部实例化
SingletonHungry() {
std::cout << "饿汉模式实例被创建" << std::endl;
}
// 禁用拷贝构造和赋值运算符(防止通过拷贝创建新实例)
SingletonHungry(const SingletonHungry&) = delete;
SingletonHungry& operator=(const SingletonHungry&) = delete;
public:
// 3. 公共静态方法:返回唯一实例
static SingletonHungry& getInstance() {
return instance;
}
void doSomething() {
std::cout << "饿汉模式:执行任务" << std::endl;
}
};
// 类外初始化静态成员(C++要求),此时实例被创建
SingletonHungry SingletonHungry::instance;
// 测试
int main() {
SingletonHungry& s1 = SingletonHungry::getInstance();
SingletonHungry& s2 = SingletonHungry::getInstance();
// 验证是否为同一实例(地址相同)
std::cout << "s1地址:" << &s1 << ", s2地址:" << &s2 << std::endl;
s1.doSomething();
return 0;
}
特点:
- 线程安全:C++ 中全局 / 静态变量的初始化在程序启动阶段(main 函数前)完成,由编译器保证线程安全(C++11 及以上)。
- 缺点:如果实例占用资源大且程序全程未使用,会造成资源浪费。
2. 懒汉模式(Lazy Initialization,饱汉模式)
核心思想 :延迟初始化,仅在第一次调用getInstance()时创建实例,需处理多线程安全问题。
(1)基础版(非线程安全)
#include <iostream>
class SingletonLazy {
private:
// 1. 私有静态指针:暂不初始化
static SingletonLazy* instance;
// 2. 私有构造函数
SingletonLazy() {
std::cout << "懒汉模式实例被创建" << std::endl;
}
// 禁用拷贝和赋值
SingletonLazy(const SingletonLazy&) = delete;
SingletonLazy& operator=(const SingletonLazy&) = delete;
public:
// 3. 公共静态方法:首次调用时创建实例
static SingletonLazy* getInstance() {
if (instance == nullptr) { // 第一次调用时初始化
instance = new SingletonLazy();
}
return instance;
}
void doSomething() {
std::cout << "懒汉模式:执行任务" << std::endl;
}
// 可选:手动释放资源(单例通常不需要,程序结束时自动释放)
static void destroyInstance() {
if (instance != nullptr) {
delete instance;
instance = nullptr;
}
}
};
// 类外初始化静态指针为nullptr
SingletonLazy* SingletonLazy::instance = nullptr;
// 测试(单线程环境下正确,多线程可能创建多个实例)
int main() {
SingletonLazy* s1 = SingletonLazy::getInstance();
SingletonLazy* s2 = SingletonLazy::getInstance();
std::cout << "s1地址:" << s1 << ", s2地址:" << s2 << std::endl;
s1->doSomething();
SingletonLazy::destroyInstance(); // 手动释放
return 0;
}
问题 :多线程环境下,多个线程可能同时进入if (instance == nullptr),导致创建多个实例(线程不安全)。
(2)线程安全版(C++11 及以上推荐)
利用 C++11 的局部静态变量初始化线程安全特性(标准规定:局部静态变量的初始化在多线程环境下是互斥的),实现简洁且安全的懒汉模式:
#include <iostream>
class SingletonLazySafe {
private:
// 私有构造函数
SingletonLazySafe() {
std::cout << "线程安全懒汉模式实例被创建" << std::endl;
}
// 禁用拷贝和赋值
SingletonLazySafe(const SingletonLazySafe&) = delete;
SingletonLazySafe& operator=(const SingletonLazySafe&) = delete;
public:
// 关键:返回局部静态变量的引用,C++11保证初始化线程安全
static SingletonLazySafe& getInstance() {
static SingletonLazySafe instance; // 首次调用时初始化,仅一次
return instance;
}
void doSomething() {
std::cout << "线程安全懒汉模式:执行任务" << std::endl;
}
};
// 测试(多线程环境下安全)
int main() {
SingletonLazySafe& s1 = SingletonLazySafe::getInstance();
SingletonLazySafe& s2 = SingletonLazySafe::getInstance();
std::cout << "s1地址:" << &s1 << ", s2地址:" << &s2 << std::endl;
s1.doSomething();
return 0;
}
特点:
- 线程安全:C++11 及以上标准保证局部静态变量的初始化是线程安全的,无需额外加锁。
- 延迟加载:仅在首次调用
getInstance()时初始化,节省资源。 - 简洁高效:避免了指针管理和手动释放的问题,推荐使用。
总结
- 饿汉模式:C++ 中通过全局静态成员初始化实现,线程安全但可能提前占用资源。
- 懒汉模式:推荐使用 C++11 的局部静态变量方式,兼顾线程安全和延迟加载,是最简洁的实现。