【Linux】单例模式及其在线程池中的应用

发布于:2025-04-11 ⋅ 阅读:(33) ⋅ 点赞:(0)

📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述


📢前言

上篇文章中笔者模拟了最基本的 线程池,其实就是将多个线程放在一起统一管理

这节课,笔者将遵循 线程安全 的原则,将 单例模式 融入 线程池 中。


🏳️‍🌈一、什么是单例模式

单例模式 是一种创建型设计模式,其核心目标是确保 一个类仅有一个实例 ,并为该实例提供全局访问点。它通过限制类的实例化次数,避免重复创建对象,常用于管理全局唯一资源(如线程池、配置管理器、日志系统等)。

  • 唯一性:类只能创建一个对象实例。
  • 全局访问:通过静态方法或全局变量提供统一的访问入口。
  • 延迟加载​(可选):实例在第一次被使用时才初始化,减少资源浪费。

我们可以对比一下即将实现的单例模式线程池 和 原来的线程池
在这里插入图片描述

  1. 单例模式下线程池禁止了 拷贝构造拷贝赋值构造
  2. 线程池构造 转移到私有
  3. 增加了 实例实例锁 的成员变量

🏳️‍🌈二、单例模式的实现方法

举个洗碗的例子

吃完饭,立刻洗碗,这种就是饿汉方式,因为下一顿吃的时候可以立刻拿着碗就能吃饭
吃完饭,先把碗放下,然后下一顿饭用到这个碗了再洗碗,就是懒汉方式

2.1 饿汉模式

  • 特点:程序启动时立即初始化实例。
  • ​优点:线程安全(无需加锁)。
  • ​缺点:可能浪费内存(未使用时也初始化)。
class Singleton {
private:
    static Singleton* instance;  // 静态成员变量
    Singleton() {}               // 私有构造函数

public:
    static Singleton* GetInstance() {
        return instance;  // 直接返回预先初始化的实例
    }
};

// 初始化静态成员(程序启动时创建)
Singleton* Singleton::instance = new Singleton();

2.2 懒汉模式

  • 特点:实例在第一次调用 GetInstance() 时创建。
  • ​优点:按需加载,节省资源。
  • ​缺点:需处理多线程安全问题。
class Singleton {
private:
    static std::atomic<Singleton*> instance;  // 原子指针
    static std::mutex mtx;                     // 互斥锁
    Singleton() {}

public:
    static Singleton* GetInstance() {
        if (instance == nullptr) {             // 第一次检查(避免频繁加锁)
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {         // 第二次检查(确保唯一性)
                instance = new Singleton();
            }
        }
        return instance;
    }
};

2.3 单例模式优缺点

在这里插入图片描述

🏳️‍🌈三、懒汉模式下单例线程池实现

  1. 单例模式下线程池禁止了 拷贝构造拷贝赋值构造
  2. 线程池构造 转移到私有
  3. 增加了 实例实例锁 的成员变量

针对这三点,我们根据上一篇文章中的线程池,需要进行一系列更改

3.1 静态线程池

我们使用 instance 控制静态线程池,以及静态线程锁

template <typename T>
    ThreadPool<T> *ThreadPool<T>::instance = NULL;

    template <typename T>
    Mutex ThreadPool<T>::_instance_mutex; // 只用来保护单例
static ThreadPool<T>* getInstance() {
    if (instance == NULL) {
        LockGuard lockguard(_instance_mutex);
        if (instance == NULL) {
            LOG(LogLevel::INFO) << "单例被首次执行,需要加载对象";
            instance = new ThreadPool<T>();
        }
    }
    return instance;
}

3.2 禁止拷贝构造 赋值拷贝构造

// 禁止拷贝构造
ThreadPool(const ThreadPool<T>&) = delete;
// 进制拷贝赋值构造
ThreadPool<T>& operator=(const ThreadPool<T>&) = delete;

3.3 整体代码

#pragma once

#include <iostream>
#include <memory>
#include <vector>
#include <queue>

#include "Cond.hpp"
#include "Mutex.hpp"
#include "Log.hpp"
#include "Thread.hpp"



namespace ThreadPoolModule{
    using namespace ThreadModule;
    using namespace CondModule;
    using namespace LockModule;
    using namespace LogModule;

    using thread_t = std::shared_ptr<Thread>;
    const static int defaultnum = 5;

    template<typename T>
    class ThreadPool{
        private:
            // 判断线程池是否为空
            bool IsEmpty() {return _resource.empty();}
            // 工作线程的主要循环逻辑
            void HandlerTask(std::string name){
                LOG(LogLevel::INFO) << "线程:" << name << ",进入HandlerTask的逻辑";
                while(true){
                    T t;
                    
                    // 等待资源
                    if(_resource.empty() && _isrunning){
                        LOG(LogLevel::INFO) << "线程:" << name << ",资源池为空,等待资源";
                        ++_wait_num;
                        _cond.Wait(_mutex);
                        --_wait_num;
                    }

                    // 任务队列为空 && 线程池停止工作
                    if(IsEmpty() && !_isrunning)
                        break;
                    
                    t = _resource.front();
                    _resource.pop();
                }
            }

            // 禁止拷贝构造
            ThreadPool(const ThreadPool<T>&) = delete;
            // 进制拷贝赋值构造
            ThreadPool<T>& operator=(const ThreadPool<T>&) = delete;

            // 构造函数
            ThreadPool(int num = defaultnum) : _num(num), _wait_num(0), _isrunning(false)
            {
                for(int i = 0; i < _num; ++i){
                    _threads.push_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));
                    LOG(LogLevel::INFO) << "线程对象 " << _threads.back()->Name() << " 构造成功";
                }
            }
        
        public:
            static ThreadPool<T>* getInstance(){
                if(instance == NULL){
                    LockGuard lockguard(_instance_mutex);
                    if(instance == NULL){
                        LOG(LogLevel::INFO) << "单例被首次执行,需要加载对象";
                        instance = new ThreadPool<T>();
                    }
                }   
                return instance;
            }

            void Equeue(T&& in){
                LockGuard lockGuard(_mutex);
                if(!_isrunning) return;
                _resource.push(std::move(in));

                if(_wait_num > 0)
                    _cond.Notify();
            }
            void Start(){
                if(_isrunning) return;

                _isrunning = true;
                for(auto& thread_ptr : _threads){
                    LOG(LogLevel::INFO) << "线程:" << thread_ptr->Name() << " 启动";
                    thread_ptr->Start();
                }
            }
            void Stop(){
                LockGuard lockguard(_mutex);
                if(_isrunning){
                    _isrunning = false;
                    if(_wait_num > 0);
                        _cond.NotifyAll();
                }
            }

            void Wait(){
                for(auto& thread_ptr : _threads){
                    thread_ptr->Join();
                    LOG(LogLevel::INFO) << "线程:" << thread_ptr->Name() << " 退出";
                }
            }

            ~ThreadPool(){}

        private:
            std::vector<thread_t> _threads;
            int _num;
            int _wait_num;

            std::queue<T> _resource;

            Mutex _mutex;
            Cond _cond;

            bool _isrunning;

            static ThreadPool<T>* instance;
            static Mutex _instance_mutex;
    };

    template <typename T>
    ThreadPool<T> *ThreadPool<T>::instance = NULL;

    template <typename T>
    Mutex ThreadPool<T>::_instance_mutex; // 只用来保护单例
}

🏳️‍🌈四、测试代码

#include <iostream>
#include <string>
#include <functional>
#include "SigThreadPool.hpp"


using namespace ThreadPoolModule;
using namespace LogModule;

using task_t = std::function<void(std::string name)>;

void Push(std::string name)
{
    LOG(LogLevel::DEBUG) << "我是一个推送数据到服务器的一个任务, 我正在被执行" << "[" << name << "]";
}

int main(){
    ENABLE_CONSOLE_LOG();
    ThreadPool<task_t>::getInstance()->Start();
    char c;
    int cnt = 5;
    while (cnt)
    {
        // std::cin >> c;
        ThreadPool<task_t>::getInstance()->Equeue(Push);
        cnt--;
        sleep(1);
    }

    ThreadPool<task_t>::getInstance()->Stop();
    ThreadPool<task_t>::getInstance()->Wait();
}

在这里插入图片描述


👥总结

本篇博文对 【Linux】单例模式及其在线程池中的应用 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述