线程池和单例模式

目录

[一. 线程池](#一. 线程池)

[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.1.核心组件定义:

2.2.核心参数设计

[2.3. 核心流程实现](#2.3. 核心流程实现)

(1)初始化线程池

(2)提交任务的处理逻辑

(3)工作线程的运行逻辑

(4)线程池的销毁

[2.4. 关键技术点](#2.4. 关键技术点)

[二. 单例模式](#二. 单例模式)

[1. 什么是设计模式](#1. 什么是设计模式)

[2. 什么是单例模式](#2. 什么是单例模式)

[1. 饿汉模式(Eager Initialization)](#1. 饿汉模式(Eager Initialization))

[2. 懒汉模式(Lazy Initialization,饱汉模式)](#2. 懒汉模式(Lazy Initialization,饱汉模式))

(1)基础版(非线程安全)

[(2)线程安全版(C++11 及以上推荐)](#(2)线程安全版(C++11 及以上推荐))

总结


一. 线程池

1. 解决了那些问题

1.1 降低资源消耗(解决创建/销毁线程的高成本问题)

线程的创建会消耗CPU资源(分配创建新的TCB,分配独立的栈空间,继承主线程的页表和文件描述,添加至调度队列,寄存器中设置独立上下文),线程的销毁亦是。所以频繁的创建和销毁会大幅度降低CPU的性能。

而线程池避免了频繁的创建和销毁,创建一定数量的线程进行复用,尤其适合短期任务频繁执行的场景

  1. TCB分配与初始化 :这不仅仅是分配一小块内存。task_struct 是一个极其庞大的结构体(在最新内核中可能有几KB大小),初始化其数十个字段本身就是不小的计算任务。

  2. 栈空间分配 :虽然分配虚拟地址空间本身很快,但物理内存的分配是"按需"通过页故障实现的。新线程开始运行时,会触发一系列的缺页中断,这会导致内核陷入、分配物理页、更新页表,成本很高。

  3. 调度器开销

    • 队列操作 :将线程加入调度队列需要锁来保护队列的一致性,这在多核环境下可能引发锁竞争

    • 缓存失效:当一个新线程被调度到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)提交任务的处理逻辑

当外部提交任务时,线程池按以下逻辑处理:

  1. 若当前线程数 < 核心线程数:直接创建新线程执行任务(即使有其他核心线程空闲,也会优先新建到核心数)。
  2. 若当前线程数 ≥ 核心线程数:将任务加入任务队列,等待空闲线程从队列中获取执行。
  3. 若任务队列已满
    • 若当前线程数 < 最大线程数:创建临时线程执行任务(临时线程空闲后会按keepAliveTime销毁)。
    • 若当前线程数 ≥ 最大线程数:触发拒绝策略处理新任务。
(3)工作线程的运行逻辑

每个工作线程(Worker)是一个循环体,逻辑如下:

复制代码
// 伪代码示意
while (true) {
    Task task = workQueue.take(); // 阻塞等待队列中的任务
    try {
        task.run(); // 执行任务
    } catch (Exception e) {
        // 处理任务执行异常
    }
}
  • 核心线程:即使任务队列空,也会一直阻塞等待新任务(不会被销毁)。
  • 临时线程:若空闲时间超过keepAliveTime,则退出循环并销毁。
(4)线程池的销毁
  • 调用销毁方法(如shutdown())时,线程池不再接收新任务,等待已提交的任务执行完毕后,中断所有工作线程,释放资源。

2.4. 关键技术点

  • 阻塞队列 :使用阻塞队列(如BlockingQueue)实现任务的缓存和线程间的同步,避免线程空轮询(通过take()阻塞等待)。
  • 线程复用:工作线程通过循环从队列取任务,避免了频繁创建 / 销毁线程的开销。
  • 并发控制 :通过锁(如ReentrantLock)或原子操作保证线程池状态(如运行、关闭)和参数修改的线程安全。
  • 动态调整:支持运行时调整核心线程数、最大线程数等参数(需配合锁保证线程安全)。
  1. 代码示例(简易)

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 的局部静态变量方式,兼顾线程安全和延迟加载,是最简洁的实现。
相关推荐
百锦再2 小时前
第1章 Rust语言概述
java·开发语言·人工智能·python·rust·go·1024程序员节
一叶之秋14122 小时前
QT背景介绍与环境搭建
开发语言·qt
java1234_小锋3 小时前
PyTorch2 Python深度学习 - 模型保存与加载
开发语言·python·深度学习·pytorch2
ACP广源盛139246256733 小时前
(ACP广源盛)GSV2231---DisplayPort 1.4 MST 到 HDMI 2.0/DP/Type-C 转换器(带嵌入式 MCU)
c语言·开发语言·单片机·嵌入式硬件·音视频·mst
quant_19863 小时前
【教程】使用加密货币行情接口 - 查询比特币实时价格
开发语言·后端·python·websocket·网络协议
熊猫_豆豆3 小时前
Python 写一个标准版和程序员版计算器
开发语言·python·计算器
Mr.Jessy3 小时前
Web APIs 学习第四天:DOM事件进阶
开发语言·前端·javascript·学习·ecmascript
studyForMokey3 小时前
【Kotlin内联函数】
android·开发语言·kotlin
小虚竹4 小时前
Rust日志系统完全指南:从log门面库到env_logger实战
开发语言·后端·rust