1. 单例模式

单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。这种模式在需要控制资源访问、管理共享状态或协调系统行为时非常有用。

单例模式的核心特点

  • 私有构造函数:防止外部通过new关键字创建实例;
  • 静态成员函数:提供全局访问点(静态成员函数getInstance());
  • 禁止拷贝和赋值:通过delete关键字禁用拷贝构造函数和赋值运算符。

单例模式的应用场景

  • 日志系统:确保所有日志都写入同一个日志文件;
  • 配置管理:全局共享一份配置信息;
  • 数据库连接池:统一管理数据库连接;
  • 设备管理器:如打印机等硬件设备的管理。

需要注意的是,单例模式虽然方便,但也会带来一些问题,如增加代码耦合度、不利于单元测试等,因此在使用时需要权衡利弊。

在 C++ 中,单例模式主要有两种实现方式:饿汉式懒汉式,它们的核心区别在于实例创建的时机不同

1.1 饿汉式实现

饿汉式在程序启动时(类加载时)就创建唯一实例,不管后续是否会使用到。

class EagerSingleton {
private:// 私有构造函数,防止外部创建实例EagerSingleton() {}// 禁用拷贝构造和赋值运算EagerSingleton(const EagerSingleton&) = delete;EagerSingleton& operator=(const EagerSingleton&) = delete;// 静态成员变量,程序启动时就初始化static EagerSingleton instance;public:// 提供全局访问点static EagerSingleton& getInstance() {return instance;}
};// 在类外初始化静态成员
EagerSingleton EagerSingleton::instance;

饿汉式特点:

  • 线程安全:由于实例在程序启动时就创建,不存在多线程竞争问题;
  • 可能浪费资源:如果单例从未被使用,也会占用内存;
  • 实现简单:不需要考虑线程同步问题。

1.2 懒汉式实现

懒汉式在第一次调用getInstance()方法时才创建实例,实现了 "延迟初始化"

class LazySingleton {
private:// 私有构造函数LazySingleton() {}// 禁用拷贝构造和赋值运算LazySingleton(const LazySingleton&) = delete;LazySingleton& operator=(const LazySingleton&) = delete;public:// 提供全局访问点,C++11后局部静态变量初始化是线程安全的static LazySingleton& getInstance() {// 第一次调用时才初始化实例static LazySingleton instance;return instance;}
};

或者:

class LazySingleton {
private:// 私有构造函数LazySingleton() {}// 私有析构函数~LazySingleton() {instance = nullptr;}// 禁用拷贝构造和赋值运算LazySingleton(const LazySingleton&) = delete;LazySingleton& operator=(const LazySingleton&) = delete;// 使用指针的方式来访问唯一实例static LazySingleton *instance;public:// 此方式无法保证线程安全,应当加锁保护static LazySingleton* const getInstance() {// 第一次调用时才初始化实例if(instance == nullptr)instance = new LazySingleton();return instance;}
};LazySingleton* LazySingleton::instance = nullptr;

前者和饿汉式一样,实例在创建之后就不会消失后者的实例则可以在被销毁之后重新申请

懒汉式特点:

  • 延迟初始化:节省资源,只有在真正需要时才创建实例;
  • 线程安全(C++11 及以上):标准保证局部静态变量的初始化是线程安全的;
  • 可能影响首次访问性能:第一次调用时需要完成初始化。

1.3 总结

饿汉式懒汉式
实例创建时机程序启动时首次使用时
线程安全性天然安全C++11 后安全
资源利用可能浪费更高效
实现复杂度简单稍复杂(需考虑线程安全)

实际开发中,懒汉式因为其资源利用效率更高而更常用,尤其是在单例可能不会被使用的场景下。而饿汉式适合在程序启动时就需要初始化的核心组件。

2. 用单例模式设计线程池

2.1 什么是线程池

线程池是一种线程管理机制,它预先创建一定数量的线程,通过复用这些线程来处理多个任务,从而避免频繁创建和销毁线程带来的性能开销,提高系统效率和资源利用率

线程池包含一个线程队列和一个任务队列

  • 线程队列:存储预先创建的空闲线程,等待处理任务。
  • 任务队列:存放待执行的任务,当线程空闲时会从队列中获取任务执行。

2.2 如何将任务传递给线程

首先,任务队列一定是定义在线程池内部的,我们要使线程访问到任务队列及保护任务队列的锁,就要使这些线程能访问到线程池本身。

所以,我们在线程池内部定义线程的运行函数,即不断循环访问任务队列获取任务:

void ThreadHandler()
{while (true){Task task;{LockGuard lockguard(_queue_mutex);while (_queue.empty() && _isrunning){_not_empty.wait(_queue_mutex);}if (_queue.empty() && !_isrunning)break;task = _queue.front();_queue.pop();}task();}LOG(LogLevel::DEBUG) << "线程[" << Thread::GetMyName() << "]退出...";
}

但是直接将该函数传递给线程是不行的。例如,在构造线程时,直接将该函数以及this传过去:

ThreadPool(unsigned int thread_num = default_thread_num): _isrunning(true)
{if (thread_num <= 0)throw ThreadPoolException("线程数量过少!");for (int i = 1; i <= thread_num; i++)_threads.emplace_back("thread-" + std::to_string(i), ThreadHandler, this);
}

编译就会出现如下报错:

这个编译错误的核心原因是:非静态成员函数(ThreadHandler)不能直接作为函数名传递给线程构造函数。非静态成员函数的调用必须依赖于一个类的实例(this指针),而直接进行传参时,编译器无法自动关联实例,因此判定为 “无效使用非静态成员函数”。

让线程执行非静态成员函数的正确方式是使用lambda表达式捕捉this并包装该函数:

[this] { ThreadHandler(); 
}

然后像这样传参:

_threads.emplace_back("thread-" + std::to_string(i), [this]{ ThreadHandler(); });

2.3 懒汉实现1

#pragma once
#include <vector>
#include <queue>
#include "Mutex.hpp"
#include "Thread.hpp"
#include "Cond.hpp"
#include "Log.hpp"using namespace MutexModule;
using namespace ThreadModule;
using namespace CondModule;
using namespace LogModule;namespace ThreadPoolModule
{class ThreadPoolException : public std::runtime_error{public:explicit ThreadPoolException(const std::string &message): std::runtime_error("ThreadPoolException: " + message) {}};static const unsigned int default_thread_num = 6;static const unsigned int default_capacity = 9;template <typename Task>class ThreadPool{private:ThreadPool(unsigned int thread_num = default_thread_num): _isrunning(true){if (thread_num <= 0)throw ThreadPoolException("线程数量过少!");for (int i = 1; i <= thread_num; i++)_threads.emplace_back("thread-" + std::to_string(i), [this]{ ThreadHandler(); });}~ThreadPool(){}ThreadPool(const ThreadPool<Task>&) = delete;ThreadPool<Task>& operator=(const ThreadPool<Task>&) = delete;void ThreadHandler(){while (true){Task task;{LockGuard lockguard(_queue_mutex);while(_queue.empty() && _isrunning){_not_empty.wait(_queue_mutex);}if(_queue.empty() && !_isrunning)break;task = _queue.front();_queue.pop();}task();}LOG(LogLevel::DEBUG) << "线程[" << Thread::GetMyName() << "]退出...";}void Start(){for (auto &thread : _threads)thread.Start();LOG(LogLevel::DEBUG) << "线程池已启动...";}public:void Stop(){_isrunning = false;_not_empty.broadcast();LOG(LogLevel::DEBUG) << "线程池开始停止...";}void Wait(){if(_isrunning)throw ThreadPoolException("等待前线程池未停止!");for (auto &thread : _threads)thread.Join();LOG(LogLevel::DEBUG) << "等待成功, 线程已全部退出...";}void PushTask(const Task &task){if(!_isrunning)throw ThreadPoolException("线程池已停止, 无法继续增加任务!");_queue.push(task);_not_empty.signal();}static ThreadPool<Task>& GetInstance(){static ThreadPool<Task> Instance;Instance.Start();return Instance;}private:bool _isrunning;std::vector<Thread> _threads;std::queue<Task> _queue;Mutex _queue_mutex;Cond _not_empty;};
}

2.4 懒汉实现2

#pragma once
#include <vector>
#include <queue>
#include "Mutex.hpp"
#include "Thread.hpp"
#include "Cond.hpp"
#include "Log.hpp"using namespace MutexModule;
using namespace ThreadModule;
using namespace CondModule;
using namespace LogModule;namespace ThreadPoolModule
{class ThreadPoolException : public std::runtime_error{public:explicit ThreadPoolException(const std::string &message): std::runtime_error("ThreadPoolException: " + message) {}};static const unsigned int default_thread_num = 6;static const unsigned int default_capacity = 9;template <typename Task>class ThreadPool{private:ThreadPool(unsigned int thread_num = default_thread_num): _isrunning(true){if (thread_num <= 0)throw ThreadPoolException("线程数量过少!");for (int i = 1; i <= thread_num; i++)_threads.emplace_back("thread-" + std::to_string(i), [this]{ ThreadHandler(); });}~ThreadPool(){_ins = nullptr;}ThreadPool(const ThreadPool<Task>&) = delete;ThreadPool<Task>& operator=(const ThreadPool<Task>&) = delete;void ThreadHandler(){while (true){Task task;{LockGuard lockguard(_queue_mutex);while(_queue.empty() && _isrunning){_not_empty.wait(_queue_mutex);}if(_queue.empty() && !_isrunning)break;task = _queue.front();_queue.pop();}task();}LOG(LogLevel::DEBUG) << "线程[" << Thread::GetMyName() << "]退出...";}void Start(){for (auto &thread : _threads)thread.Start();LOG(LogLevel::DEBUG) << "线程池已启动...";}public:void Stop(){_isrunning = false;_not_empty.broadcast();LOG(LogLevel::DEBUG) << "线程池开始停止...";}void Wait(){if(_isrunning)throw ThreadPoolException("等待前线程池未停止!");for (auto &thread : _threads)thread.Join();delete _ins;LOG(LogLevel::DEBUG) << "等待成功, 线程已全部退出...";}void PushTask(const Task &task){if(!_isrunning)throw ThreadPoolException("线程池已停止, 无法继续增加任务!");_queue.push(task);_not_empty.signal();}static ThreadPool<Task> *const GetInstance(){if (_ins == nullptr){LockGuard lockguard(_mutex);if (_ins == nullptr){try{_ins = new ThreadPool<Task>();}catch (const std::exception &e){std::cerr << e.what() << '\n';}_ins->Start();}}return _ins;}private:bool _isrunning;std::vector<Thread> _threads;std::queue<Task> _queue;static ThreadPool<Task> *_ins;Mutex _queue_mutex;static Mutex _mutex;Cond _not_empty;};template <typename Task>ThreadPool<Task> *ThreadPool<Task>::_ins = nullptr;template <typename Task>Mutex ThreadPool<Task>::_mutex;
}

相比较于第一种,第二种可以在调用Stop以及Wait函数之后重新启动。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/96628.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/96628.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/96628.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Chrome和Edge如何开启暗黑模式

Edge和Chrome浏览器都提供了实验性功能&#xff0c;可以通过修改实验性设置来开启暗黑模式。 在浏览器地址栏中输入edge://flags/&#xff08;Edge&#xff09;或chrome://flags/&#xff08;Chrome&#xff09;。在搜索框中输入“dark”&#xff0c;找到与暗黑模式相关的选项。…

【科研绘图系列】浮游植物的溶解性有机碳与初级生产力的关系

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍 数据准备 数据处理 溶解性有机碳(DOC)与初级生产力(NPP)的关系 溶解性有机碳(DOC)与光照强度(PAR)的关系 数据可视化 加载R包 数据下载 导入数据 画图1 画图2 总结 系统信…

IDEA相关的设置和技巧

IDEA相关的设置和技巧 我的博客对应文章地址 1.布局设置 IDEA的布局自定义程度很高&#xff0c;顶部工具栏&#xff0c;侧边栏都可以随意定制&#xff0c;设置好的布局方案可以保存&#xff0c;在新项目中快速使用 1.1 工具栏设置 [!tip] 举个例子&#xff1a;比如我要在顶部…

AWS Lambda 完全指南:解锁无服务器架构的强大力量

在云计算的发展浪潮中,无服务器(Serverless) 架构已然成为构建现代应用的新范式。而在这场变革的中心,AWS Lambda 作为开创性的 Function-as-a-Service (FaaS) 服务,彻底改变了我们部署和运行代码的方式。 本文将带您深入探索 AWS Lambda,从核心概念、工作原理到高级实践…

人工智能时代下普遍基本收入(UBI)试验的实践与探索——以美国硅谷试点为例

一、硅谷UBI试验的最新进展&#xff08;2025年&#xff09;1. 试验规模与资金来源圣克拉拉县试点&#xff1a;硅谷所在地圣克拉拉县针对脱离寄养家庭的年轻人开展UBI试验&#xff0c;每月发放1000美元补贴&#xff0c;持续1-2年&#xff0c;覆盖约60名参与者&#xff0c;成本约…

云计算之云主机Linux是什么?有何配置?如何选?

一、云环境如何选择Linux发行版 1.1、Linux在各个领域的发展 Linux在各个领域的发展序号Linux发展领域说明1Linux在服务器领域的发展目前Linux在服务器领域已经占据95%的市场份额&#xff0c;同时Linux在服务器市场的迅速崛起&#xff0c;已经引起全球IT产业的高度关注&#xf…

XCVU13P-2FHGB2104E Xilinx(AMD)Virtex UltraScale+ FPGA

XCVU13P-2FHGB2104E 是 Xilinx&#xff08;AMD&#xff09;Virtex UltraScale FPGA 系列中的一款高性能芯片&#xff0c;适用于需要大量逻辑资源、高带宽和高速数据传输的应用场景。作为该系列中的旗舰产品&#xff0c;XCVU13P-2FHGB2104I 结合了强大的处理能力和灵活的可编程性…

自动化单词例句获取系统设计方案

方案一 (网络爬虫) 这个方案的核心思路是:创建一个自动化的脚本,该脚本会读取你 MongoDB 中的单词,然后去一个免费的在线词典网站上抓取这些单词的例句,最后将抓取到的例句存回你的 MongoDB 数据库中对应的单词条目下。 一、 核心思路与技术选型 自动化脚本: 我们将使用 P…

WPF Alert弹框控件 - 完全使用指南

WPF Alert弹框控件 - 完全使用指南概述快速开始nuget安装与引用基本用法功能特性详细说明AlertType 枚举方法参数详解Show 方法&#xff08;局部弹窗&#xff09;ShowGlobal 方法&#xff08;全局弹窗&#xff09;完整示例代码XAML 布局C# 代码实现界面演示功能特性对比表格自定…

可视化-模块1-HTML-01

1-软件下载&#xff1a; 软件名称&#xff1a;HBuilderX 官网地址&#xff1a; https://www.dcloud.io/hbuilderx.html 下载文佳-解压缩-打开exe文件 创建快捷方式至桌面 2-创建项目 【普通项目】-【基本HTML项目】-【项目名&#xff1a;week1-1】 【index】输入&#xff1…

机器翻译 (Machine Translation) 经典面试笔试50题(包括详细答案)

更多内容请见: 机器翻译修炼-专栏介绍和目录 文章目录 第一部分:基础理论与概念 (1-15题) 1. 题目: 什么是机器翻译(MT)?请简述其发展历程中的几个主要范式。 2. 题目: 机器翻译的主要评价指标有哪些?请详细解释BLEU指标的计算原理和优缺点。 3. 题目: 什么是平行语料…

linux中文本文件操作之grep命令

文章目录背景案例demo环境方式一、安装wsl方式二、安装grep一、查找指定字符串二、忽略大小写查找三、查找时显示行号四、统计匹配的次数五、精准匹配一个单词六、显示匹配上下文七、只显示匹配的内容八、按固定字符串匹配背景 在日常运维中会对日志文件&#xff0c;使用grep命…

链表漫游指南:C++ 指针操作的艺术与实践

文章目录0. 前言1. 链表的分类2. 单链表的实现2.1 链表的基本结构——节点&#xff08;Node&#xff09;2.2 核心操作详解2.2.1 构造和析构2.2.2 插入操作2.2.3 删除操作2.3.4 其他操作2.4 总结3. 双向链表的实现3.1 基本结构设计3.2 基本操作3.2.1 初始化与销毁3.2.2 插入与删…

Claude Code赋能企业级开发:外卖平台核心系统的智能化重构

开篇&#xff1a;万亿市场背后的技术挑战中国外卖市场日订单量超过1亿单&#xff0c;每一单背后都是一个复杂的技术链条&#xff1a;用户下单→商家接单→骑手抢单→实时配送→评价反馈。构建这样一个支撑千万级并发、涉及地理位置计算、实时调度、支付结算的超级平台&#xff…

【使用Unsloth 微调】数据集的种类

1. 什么是数据集 对于大型语言模型&#xff08;LLMs&#xff09;&#xff0c;数据集是用于训练模型的数据集合。为了训练有效&#xff0c;文本数据需要能够被分词&#xff08;tokenized&#xff09;。创建数据集的关键部分之一是聊天模板&#xff08;chat template&#xff09;…

【码蹄杯】2025年本科组省赛第一场

个人主页&#xff1a;Guiat 归属专栏&#xff1a;算法竞赛 文章目录1. MC0455 四大名著-西游签到2. MC0456 斩断灵藤3. MC0457 符咒封印4. MC0458 移铁术5. MC0459 昆仑墟6. MC0460 星空迷轨阵7. MC0461 排队8. MC0462 最后一难正文 总共8道题。 1. MC0455 四大名著-西…

CentOS 10安装Ollama

前置说明 linux服务器版本&#xff1a;CentOS10 ollama版本&#xff1a;v0.11.6 下载安装包 下载安装包 官网地址&#xff1a;Ollama 下载地址&#xff1a;Download Ollama 选择linux平台&#xff0c;由于使用官网提供的脚本直接安装容易失败&#xff0c;这里选择手动下…

手机、电脑屏幕的显示坏点检测和成像原理

如今&#xff0c;手机和电脑屏幕已成为人们日常生活和工作中不可或缺的一部分。无论是处理文档、观看视频&#xff0c;还是进行专业设计&#xff0c;屏幕的显示质量都直接影响着用户体验。本文将介绍屏幕显示的基本原理&#xff0c;包括RGB色素构成和成像机制&#xff0c;并进一…

文件与fd

文件与fd一、前置预备二、复习c语言文件三、系统文件认识3.1 系统层面有关文件的接口&#xff08;open&#xff09;&#xff1a;![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b15577967d1445b08cd5252f2009683a.png)3.2 简单使用open参数3.3 语言vs系统3.4 进一步理…

语义通信高斯信道仿真代码

1️⃣ 代码 def AWGN(coding, snr, devicecpu):"""为输入张量添加高斯白噪声&#xff08;AWGN&#xff09;&#xff0c;根据指定的 SNR&#xff08;分贝&#xff09;控制噪声强度。参数&#xff1a;coding (torch.Tensor): 输入张量&#xff0c;形状为 [batch_s…