【Linux】单例模式及其在线程池中的应用
📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
- 📢前言
- 🏳️🌈一、什么是单例模式
- 🏳️🌈二、单例模式的实现方法
- 2.1 饿汉模式
- 2.2 懒汉模式
- 2.3 单例模式优缺点
- 🏳️🌈三、懒汉模式下单例线程池实现
- 3.1 静态线程池
- 3.2 禁止拷贝构造 赋值拷贝构造
- 3.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 单例模式优缺点
🏳️🌈三、懒汉模式下单例线程池实现
- 单例模式下线程池禁止了 拷贝构造 和 拷贝赋值构造
- 将 线程池构造 转移到私有
- 增加了 实例 和 实例锁 的成员变量
针对这三点,我们根据上一篇文章中的线程池,需要进行一系列更改
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】单例模式及其在线程池中的应用 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~