25.单例模式实现线程池

一、线程池的概念

1.1 线程池的介绍

线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

1.2 线程池的应用场景

当需要大量的线程来完成任务,且完成任务的时间比较短。 比如WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。但对于长时间的任务,比如⼀个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
对性能要求苛刻的应用。 比如要求服务器迅速响应客户请求。
接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。 突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数码最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

1.3 线程池的种类

• 创建固定数量线程池,循环从任务队列中获取任务对象,获取到任务对象后,执行任务对象中的任务接口。
• 浮动线程池,其他同上。

此处,我们选择固定线程个数的线程池。
在这里插入图片描述

二、线程池的简单实现

//ThreadPool.hpp
#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <memory>
#include <functional>
#include <unistd.h>
#include "log.hpp"    // 引入日志
#include "thread.hpp" // 引入线程
#include "mutex.hpp"  // 引入锁
#include "cond.hpp"   // 引入条件变量namespace ThreadPoolModual
{using namespace ThreadModual;using namespace CondModul;using namespace MutexModel;using namespace LogMudual;const static int gdefaultthreadnum = 10;template <typename T>class ThreadPool{private:// 类的成员方法,也可以成为另一个类的回调方法,方便我们继续类级别的互相调用!void HandlerTask(){LOG(LogLevel::INFO) << "entering HandlerTask modual ...";while (true){LockGuard lock(_mutex);T t;{// 1. 保证队列安全// 2. 队列中不一定有数据while (_task_queue.empty() && _isrunning){_waitnum++;_cond.Wait(_mutex);_waitnum--;}// 2.1 如果线程池已经退出了 && 任务队列是空的if (_task_queue.empty() && !_isrunning)break;// 2.2 如果线程池不退出 && 任务队列不是空的// 2.3 如果线程池已经退出 && 任务队列不是空的 --- 处理完所有的任务,然后退出// 3. ⼀定有任务, 处理任务t = _task_queue.front();_task_queue.pop();}// 4. 处理任务,这个任务属于线程独占的任务t();}}public:ThreadPool(int threadnum = gdefaultthreadnum) : _threadnum(threadnum),_waitnum(0),_isrunning(false){// 创建线程池for (int num = 0; num < _threadnum; num++){_threads.emplace_back(std::bind(&ThreadPool::HandlerTask, this));LOG(LogLevel::INFO) << "init thread " << _threads.back().Name() << " done";}}void Start(){if (_isrunning)return;_isrunning = true;for (auto &thread : _threads){thread.Start();LOG(LogLevel::INFO) << "start thread " << thread.Name() << "done";}}void Stop(){LockGuard lock(_mutex);if (_isrunning){_isrunning = false;if (_waitnum > 0)_cond.NotifyAll();}LOG(LogLevel::DEBUG) << "threadpool is exitting...";}void Wait(){for (auto &thread : _threads){thread.Join();LOG(LogLevel::INFO) << "Recycling " << thread.Name() << " done";}}void Enqueue(const T &t){LockGuard lock(_mutex);_task_queue.push(t);if (_waitnum > 0)_cond.Notify();}~ThreadPool(){}private:int _threadnum;std::vector<Thread> _threads;std::queue<T> _task_queue;Mutex _mutex;Cond _cond;int _waitnum;bool _isrunning;};
}
//ThreadPool.cc
#include "ThreadPool.hpp"using namespace ThreadPoolModual;
using task_t = function<void()>;void func()
{sleep(1);
}int main()
{ENABLE_CONSOLE_LOG_STRATEGY();std::unique_ptr<ThreadPool<task_t>> tp = std::make_unique<ThreadPool<task_t>>();tp->Start();int cnt = 10;char c;while (cnt){tp->Enqueue(func);cnt--;sleep(1);}tp->Stop();tp->Wait();return 0;
}

三、单例模式

对于某些类(如上面所写的线程池)只应该具有一个对象(实例)就称之为单例。

单例模式的实现方式有两种:懒汉方式实现和饿汉方式实现。

3.1 饿汉方式实现单例

吃完饭,立刻洗碗, 这种就是饿汉方式。 因为下一顿吃的时候可以拿着碗就能吃饭。即在程序运行起来之后就加载到内存中。

template <typename T>
class Singleton 
{static T data;
public:static T* GetInstance() {return &data;}
};

3.2 懒汉方式实现单例

吃完饭,先把碗放下,然后下一顿饭用到这个碗了再洗就是懒汉方式。即延迟实现。

template <typename T>
class Singleton 
{static T* inst;
public:static T* GetInstance(){if (inst == NULL) inst = new T();return inst;}
};

但是这存在着线程不安全的问题。第一次调用GetInstance的时候,如果两个线程同时调用,可能会创建出两份T对象的实例,但是后续再次调用就没有问题了。

// 懒汉模式, 线程安全 
template <typename T>
class Singleton 
{volatile static T* inst; // 需要设置 volatile 关键字, 否则可能被编译器优化. static std::mutex lock;
public:static T* GetInstance() {if (inst == NULL) // 双重判定空指针, 降低锁冲突的概率, 提高性能. { lock.lock(); // 使⽤互斥锁来保证多线程情况下也只调⽤⼀次 new . if (inst == NULL) inst = new T();lock.unlock();}return inst;}
};

四、线程池的单例模式

//ThreadPool.hpp
#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <memory>
#include <functional>
#include <unistd.h>
#include "log.hpp"    // 引入日志
#include "thread.hpp" // 引入线程
#include "mutex.hpp"  // 引入锁
#include "cond.hpp"   // 引入条件变量namespace ThreadPoolModual
{using namespace ThreadModual;using namespace CondModul;using namespace MutexModel;using namespace LogMudual;const static int gdefaultthreadnum = 10;template <typename T>class ThreadPool{private:// 类的成员方法,也可以成为另一个类的回调方法,方便我们继续类级别的互相调用!void HandlerTask(){LOG(LogLevel::INFO) << "entering HandlerTask modual ...";while (true){LockGuard lock(_mutex);T t;{// 1. 保证队列安全// 2. 队列中不一定有数据while (_task_queue.empty() && _isrunning){_waitnum++;_cond.Wait(_mutex);_waitnum--;}// 2.1 如果线程池已经退出了 && 任务队列是空的if (_task_queue.empty() && !_isrunning)break;// 2.2 如果线程池不退出 && 任务队列不是空的// 2.3 如果线程池已经退出 && 任务队列不是空的 --- 处理完所有的任务,然后退出// 3. ⼀定有任务, 处理任务t = _task_queue.front();_task_queue.pop();}// 4. 处理任务,这个任务属于线程独占的任务t();}}//私有实现ThreadPool(int threadnum = gdefaultthreadnum) : _threadnum(threadnum),_waitnum(0),_isrunning(false){// 创建线程池for (int num = 0; num < _threadnum; num++){_threads.emplace_back(std::bind(&ThreadPool::HandlerTask, this));LOG(LogLevel::INFO) << "init thread " << _threads.back().Name() << " done";}}// 复制拷贝禁用赋值ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;ThreadPool(const ThreadPool<T> &) = delete;public:static ThreadPool<T> *GetInstance(){if (_instance == nullptr){LockGuard lock(_lock);if (_instance == nullptr){LOG(LogLevel::INFO) << "单例首次启动";_instance = new ThreadPool<T>();}}return _instance;}void Start(){if (_isrunning)return;_isrunning = true;for (auto &thread : _threads){thread.Start();LOG(LogLevel::INFO) << "start thread " << thread.Name() << "done";}}void Stop(){LockGuard lock(_mutex);if (_isrunning){_isrunning = false;if (_waitnum > 0)_cond.NotifyAll();}LOG(LogLevel::DEBUG) << "threadpool is exitting...";}void Wait(){for (auto &thread : _threads){thread.Join();LOG(LogLevel::INFO) << "Recycling " << thread.Name() << " done";}}void Enqueue(const T &t){LockGuard lock(_mutex);_task_queue.push(t);if (_waitnum > 0)_cond.Notify();}~ThreadPool(){}private:int _threadnum;std::vector<Thread> _threads;std::queue<T> _task_queue;Mutex _mutex;Cond _cond;int _waitnum;bool _isrunning;// 添加单例模式static ThreadPool<T> *_instance;static Mutex _lock;};template <typename T>ThreadPool<T> *ThreadPool<T>::_instance = nullptr;template <typename T>Mutex ThreadPool<T>::_lock;
}
//ThreadPool.cc
#include "ThreadPool.hpp"using namespace ThreadPoolModual;
using task_t = function<void()>;
void func()
{sleep(1);
}
int main()
{ENABLE_CONSOLE_LOG_STRATEGY();int cnt = 10;while (cnt){ThreadPool<task_t>::GetInstance()->Enqueue(func);cnt--;}ThreadPool<task_t>::GetInstance()->Stop();ThreadPool<task_t>::GetInstance()->Wait();return 0;
}

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

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

相关文章

基于springboot的免税商品优选购物商城(020)

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff0c;免税商品优选购物商城当然也不能排除在外&#xff0c;随着购物商城的不断成熟&#xff0c;它彻底改变了过去传统的免税商品优选购物商城方式&…

RIP路由欺骗攻击与防御实验详解

一、基础网络配置 1. 路由器R1配置 interface GigabitEthernet0/0/0ip address 192.1.2.254 255.255.255.0 ! interface GigabitEthernet0/0/1ip address 192.1.3.254 255.255.255.0 ! router rip 1version 2network 192.1.2.0network 192.1.3.0 2. 路由器R2配置 interface…

整理和总结微信小程序的高频知识点

前言 近期萌生了一些想法&#xff0c;感觉可以做一个小程序作为产出。 但小程序做得比较少&#xff0c;因此边做边复习。整理和总结了一些高频知识点和大家一起分享。 一、模板和组件 1.1模板&#xff08;Template&#xff09; 优势 简单灵活&#xff1a;模板定义和使用都较…

如何检查CMS建站系统的插件是否安全?

检查好CMS建站系统的插件安全是确保网站安全的重要环节&#xff0c;对于常见的安全检查&#xff0c;大家可以利用以下几种有效的方法和工具&#xff0c;来帮你评估插件的安全性。 1. 检查插件来源和开发者信誉 选择可信来源&#xff1a;仅从官方插件库或可信的第三方开发者处…

RAG优化:利用python实现上下文感知(扩展)增强检索效果

检索增强生成(RAG)通过从外部知识源检索相关信息来增强AI的响应能力。传统的检索方法通常返回孤立的文本片段,这可能导致回答不完整。 为了解决这个问题,我们引入了基于上下文的检索方法,确保检索到的信息包含相邻的文本片段,以提高回答的连贯性。 通过结合重叠分块、上…

在 macOS Sequoia 15.2 中启用「三指拖动」并实现快速复制的完整指南 ✨

在 macOS Sequoia 15.2 中启用「三指拖动」并实现快速复制的完整指南 &#x1f34e;✨ 适用系统&#xff1a;macOS Sequoia 版本15.2 及以上 一、功能简介 &#x1f31f; 通过「三指拖动」手势&#xff0c;你可以轻松完成以下操作&#xff1a; • 移动文件/文本&#xff1a;直…

LeetCode 2614.对角线上的质数:遍历(质数判断)

【LetMeFly】2614.对角线上的质数&#xff1a;遍历(质数判断) 力扣题目链接&#xff1a;https://leetcode.cn/problems/prime-in-diagonal/ 给你一个下标从 0 开始的二维整数数组 nums 。 返回位于 nums 至少一条 对角线 上的最大 质数 。如果任一对角线上均不存在质数&…

基于Java(Springboot+Gradle+Mybatis+templeaf 框架)+Mysql构建的(Web)校园二手平台系统

二手市场 1 系统分析 1.1 需求分析 项目背景 国内最大的二手服务商“易趣、淘宝”其注册用户有 61% 为在校大学生&#xff0c;其他占 25% 为社会人士注册&#xff0c;他们每年与学生的交易量占总交易量的 85% 以上. “易淘”均不向交易双方任何用户提供商品质保和售后服务…

ue5蓝图项目转换为c++项目 遇到的问题

蓝图项目转c项目 工具/新建C类&#xff0c;随便新建一个c类&#xff0c;即可从蓝图项目转换为c项目 如果转换正常&#xff0c;UE5会要求重新编译程序&#xff0c;并在编译完后自动打开VS 转换前要备份 转换失败的原因 电脑上必须安装了.Net6.0&#xff0c;其他版本高了低了…

挖矿------获取以太坊测试币

文章目录 挖矿------获取以太坊测试币通过水龙头获取以太坊测试币了解Sepolia是什么&#xff1f;水龙头&#xff08;Faucet&#xff09;是什么&#xff1f;Gitcoin Passport是什么&#xff1f; 操作1.MetaMask钱包2.将MetaMask切换到Sepolia测试网络3.用MetaMask连接Gitcoin Pa…

玩转物联网-4G模块如何快速将数据上传到巴法云(TCP篇)

目录 1 前言 2 环境搭建 2.1 硬件准备 2.2 软件准备 2.3 硬件连接 2.4 检查驱动 3 巴法云平台设备创建 3.1 创建账号 3.2 进入巴法云 3.3 获取联网参数 4 连接巴法云 4.1 打开配置工具读取基本信息 4.2 设置连接参数进行数据交互 4.2.1 建立TCP连接 4.2.2 订阅主题 4.2.3 发布信…

Vue3 在组件中判断事件是否注册

效果 用途 我想用是否注册事件&#xff0c;来控制组件中图标的显示与隐藏 实现 通过组件中判断是否注册了相应的函数&#xff0c;来判断 const checkEvent () > {const instance getCurrentInstance();console.log(instance?.vnode?.props:>, instance?.vnode?…

ssh连接解析时间过长如何解决

[rootkvm ~]# vim /etc/ssh/sshd_config #修改配置 [rootkvm ~]# systemctl restart sshd #重启服务

【Linux】——进程状态僵尸进程孤儿进程

目录 前言 基本进程状态 运行状态 阻塞状态 挂起状态 Linux下的进程状态 僵尸进程 孤儿进程 结语 前言 进程的状态反映了它在执行过程中的不同阶段&#xff0c;例如创建、就绪、运行、阻塞和终止等。这些状态之间的转换由操作系统的调度算法和进程的行为共同决定。通…

信创系统极速文件查找:locate 命令详解

原文链接&#xff1a;信创系统极速文件查找&#xff1a;locate 命令详解 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇信创终端操作系统上 locate 命令详解的文章。在 Linux 及信创终端操作系统&#xff08;如 统信 UOS、麒麟 KOS&#xff09;中&#xff0c;查找…

鸿蒙数据持久化之首选项

场景介绍 用户首选项为应用提供Key-Value键值型的数据处理能力&#xff0c;支持应用持久化轻量级数据&#xff0c;并对其修改和查询。当用户希望有一个全局唯一存储的地方&#xff0c;可以采用用户首选项来进行存储。Preferences会将该数据缓存在内存中&#xff0c;当用户读取…

PyTorch分布式训练中各节点如何通信

深度学习 文章目录 深度学习前言pytorch如何初始化分布式训练怎么知道要使用哪几台机器进行训练的如何根据标识进行初始化&#xff08;init_method&#xff09;如何获取进程的唯一标识rank如何实现数据如何分发 前言 同学们在处理分布式训练时经常会遇到以下几个疑问&#xff…

[数据结构]排序之 归并排序(有详细的递归图解)

一、非递归 基本思想&#xff1a; 归并排序&#xff08; MERGE-SORT &#xff09;是建立在归并操作上的一种有效的排序算法 , 该算法是采用分治法&#xff08; Divide andConquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#x…

在本地跑通spark环境

官网下载spark 下载spark 解压就好 本地配置环境变量 配置环境变量&#xff08;系统环境变量&#xff09; 新增 SPARK_HOME 变量名&#xff1a;SPARK_HOME 变量值&#xff1a;F:\class\spark\Spark_env\spark-3.4.4-bin-hadoop3 配置 PATH&#xff0c;新增如下&#xff1a…

UE5材质法线强度控制节点FlattenNormal

连法 FlattenNormal内部是这样的 FlattenNormal的作用是用来调整法线强度 连上FlattenNormal后 拉高数值