线程池单例模式

线程池的概念                                                              

线程池是一种线程使用模式。 

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。

  • 这避免了在处理短时间任务时创建与销毁线程的代价。
  • 线程池不仅能够保证内核的充分利用,还能防止过分调度。

tips:可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

线程池的应用场景

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

线程池的实现

下面我们实现一个简单的线程池,线程池中提供了一个任务队列,以及若干个线程(多线程)。

  • 线程池中的多个线程负责从任务队列当中拿任务,并将拿到的任务进行处理。
  • 线程池对外提供一个Push接口,用于让外部线程能够将任务Push到任务队列当中。

线程池代码如下:

#include <iostream>
#include <pthread.h>
#include <vector>
#include <queue>
#include <string>static const int defaultnum = 5;struct ThreadInfo
{pthread_t tids;std::string NameThread;
};template<class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&lock);}void UnLock(){pthread_mutex_unlock(&lock);}void Wait(){pthread_cond_wait(&cond, &lock);}void WakeUp(){pthread_cond_signal(&cond);}bool IsEmptyTask(){return _TasksQueue.size() == 0;}public:ThreadPool(int num = defaultnum): _threads(num){pthread_mutex_init(&lock, nullptr);pthread_cond_init(&cond, nullptr);}void ThreadStart(){int n = _threads.size();for(int i = 0; i < n; i++){pthread_create(&_threads[i].tids, nullptr, ThreadTasks, this);_threads[i].NameThread = "thread-" + std::to_string(i);}}std::string GetThreadName(pthread_t tid){for(auto& e : _threads){if(tid == e.tids){return e.NameThread; }}return "none";}static void *ThreadTasks(void* args){ThreadPool<T>* TP = static_cast<ThreadPool<T>*>(args);while(true){std::string Name = TP->GetThreadName(pthread_self());TP->Lock();while(TP->IsEmptyTask()){TP->Wait();}T t = TP->pop();TP->UnLock();t();std::cout << Name.c_str() << ' ' << std::endl;t.GetTask();}}T pop(){T t = _TasksQueue.front();_TasksQueue.pop();return t;}void push(const T &task){Lock();_TasksQueue.push(task);WakeUp();UnLock();}~ThreadPool(){pthread_mutex_destroy(&lock);pthread_cond_destroy(&cond);}
private:std::vector<ThreadInfo> _threads;std::queue<T> _TasksQueue;pthread_mutex_t lock;pthread_cond_t cond;
};

为什么线程池中需要有互斥锁和条件变量?

使用互斥锁的原因:STL容器一开始被设计时,就是为了追求效率,并没有考虑线程安全,多线程场景,pop数据时可能产生并发问题

使用条件变量的原因:

为什么线程池中的线程执行例程需要设置为静态方法?

Routine作为类的成员函数,该函数的第一个参数是隐藏的this指针,因此这里的Routine函数,虽然看起来只有一个参数,而实际上它有两个参数,此时直接将该Routine函数作为创建线程时的执行例程是不行的,无法通过编译。

静态成员函数属于类,而不属于某个对象,也就是说静态成员函数是没有隐藏的this指针的,因此我们需要将Routine设置为静态方法,此时Routine函数才真正只有一个参数类型为void*的参数。

但是在静态成员函数内部无法调用非静态成员函数,而我们需要在Routine函数当中调用该类的某些非静态成员函数,比如Pop。因此我们需要在创建线程时,向Routine函数传入的当前对象的this指针,此时我们就能够通过该this指针在Routine函数内部调用非静态成员函数了。

任务类型的设计

#pragma once#include "ThreadPool.hpp"enum
{EXITCODE = 0,DIVZERO,MODZERO
};class Task
{
public:Task(int x, int y, char oper, int exitcode_ = EXITCODE) : _data1(x), _data2(y), _oper(oper), exitcode(exitcode_){}void run(){switch (_oper){case '+':result = _data1 + _data2;break;case '-':result = _data1 - _data2;break;case '*':result = _data1 * _data2;break;case '/':if(_data1 == 0 | _data2 == 0){exitcode = DIVZERO;}else{result = _data1 / _data2;}break;case '%':if(_data1 == 0 | _data2 == 0){exitcode = MODZERO;}else{result = _data1 % _data2;}break;default:std::cout << "Symbol mismatch!" << std::endl;break;}SolveTask();}std::string _To_String(){std::string str;str += "[exitcode: ";str += std::to_string(exitcode);str += "]";str += " ";str += std::to_string(_data1);str += " ";str += _oper;str += " ";str += std::to_string(_data2);return str;}void GetTask(){std::cout << _data1 << " " << _oper << " " << _data2 << " = ?" << std::endl;}void SolveTask(){std::cout << _To_String() << " = " << result << std::endl;}void operator()(){run();}~Task(){}private:int _data1;int _data2;char _oper;int exitcode;int result;
};

主线程逻辑

主线程就负责不断向任务队列当中Push任务就行了,此后线程池当中的线程会从任务队列当中获取到这些任务并进行处理。

#include "SingletonThreadPool.hpp"
#include "Task.hpp"
#include <unistd.h>std::string oper = "+-*/%";int main()
{srand(time(nullptr));SingletonThreadPool<Task>* STP = new SingletonThreadPool<Task>(5);STP->ThreadStart();int len = oper.size(); while(true){sleep(1);int data1 = rand() % 10;int data2 = rand() % 10 + 1;char op = oper[rand() % len];Task t(data1, data2, op);t.push(t);t.GetTask();}return 0;
}

运行代码后一瞬间就有六个线程,其中一个是主线程,另外五个是线程池内处理任务的线程。

注意: 此后我们如果想让线程池处理其他不同的任务请求时,我们只需要提供一个任务类,在该任务类当中提供对应的任务处理方法就行了。

线程安全的单例模式

什么是单例模式

单例模式是一种 "经典的, 常用的, 常考的" 设计模式.

什么是设计模式

IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式

单例模式的特点

某些类, 只应该具有一个对象(实例), 就称之为单例. 在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中. 此时往往要用一个单例的类来管理这些数据.

饿汉实现方式和懒汉实现方式

饿汉方式就是直接在类中将需要使用的对象先申请好

懒汉方式最核心的思想是 "延时加载". 从而能够优化服务器的启动速度.

例如:

#pragma onceclass Singleton
{
public:Singleton(){std::cout << "对象创建成功" << std::endl;}static Singleton& GetInstance(){return num;}private:static Singleton num;
};
#include <iostream>using namespace std;
#include "singleton.hpp"//static int num = 10;Singleton init1;
Singleton init2;
Singleton init3;
Singleton init4;
Singleton init5;
Singleton init6;int main()
{printf("启动!!!\n");    return 0;
}

可以看到饿汉式的单例模式会在程序启动之前对象就创建好了

饿汉方式实现单例模式

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

只要通过 Singleton 这个包装类来使用 T 对象, 则一个进程中只有一个 T 对象的实例

懒汉方式实现单例模式

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)  //避免占用CPU和操作系统资源,做无意义的事,提高效率{  lock.lock();          // 使用互斥锁, 保证多线程情况下也只调用一次 new.if (inst == NULL) { inst = new T(); } lock.unlock(); } return inst; } 
}; 

注意事项:

1. 加锁解锁的位置

2. 双重 if 判定, 避免不必要的锁竞争

3. volatile关键字防止过度优化

将上述线程池代码改为单例模式

#pragma once#include <iostream>
#include <pthread.h>
#include <queue>
#include <vector>
#include <string>static const int defaultnum = 5;class ThreadInfo
{
public:pthread_t tid;std::string threadname;
};template <class T>
class SingletonThreadPool
{void Lock(){pthread_mutex_lock(&lock);}void UnLock(){pthread_mutex_unlock(&lock);}void Wait(){pthread_cond_wait(&cond, &lock);}void WakeUp(){pthread_cond_signal(&cond);}bool IsEmptyThreadPool(){return _tasksqueue.size() == 0;}public:static void* RoutineTasks(void* args){SingletonThreadPool<T> *TP = static_cast<SingletonThreadPool<T>*>(args);while(true){std::string name = TP->GetThreadName(pthread_self());TP->Lock();if(TP->IsEmptyThreadPool()){TP->Wait();}T t = TP->pop();TP->UnLock();t();std::cout << name << ' ' << std::endl;t.GetTask();}}public:void ThreadStart(){int num = _threads.size();for(int i = 0; i < num; i++){pthread_create(&_threads[i].tid, nullptr, RoutineTasks, this);_threads[i].threadname = "thread-" + std::to_string(i);}}T pop(){T task = _tasksqueue.front();_tasksqueue.pop();return task;}std::string GetThreadName(pthread_t tid){for(const auto& e : _threads){if(tid == e.tid)return e.threadname;}return "none";}void push(const T& task){Lock();_tasksqueue.push(task);WakeUp();UnLock();}static SingletonThreadPool<T>* GetInStance(){//避免占用CPU和操作系统资源,做无意义的事,提高效率if(inst == nullptr){//避免多线程模式下,同时申请多个instpthread_mutex_lock(&slock);if(inst == nullptr){inst = new SingletonThreadPool<T>;}pthread_mutex_unlock(&slock);}return inst;}private:SingletonThreadPool(int num = defaultnum):_threads(num){pthread_mutex_init(&lock, nullptr);pthread_cond_init(&cond, nullptr);}~SingletonThreadPool(){pthread_mutex_destroy(&lock);pthread_cond_destroy(&cond);}//防拷贝SingletonThreadPool(const SingletonThreadPool<T>& STP) = delete;SingletonThreadPool<T>& operator=(const SingletonThreadPool<T>& STP) = delete;private:std::vector<ThreadInfo> _threads;std::queue<T> _tasksqueue;pthread_mutex_t lock;pthread_cond_t cond;static SingletonThreadPool<T> *inst;static pthread_mutex_t slock;
}; template<class T>
SingletonThreadPool<T>* SingletonThreadPool<T>::inst = nullptr;template<class T>
pthread_mutex_t SingletonThreadPool<T>::slock = PTHREAD_MUTEX_INITIALIZER;
#include "SingletonThreadPool.hpp"
#include "Task.hpp"
#include <unistd.h>std::string oper = "+-*/%";int main()
{srand(time(nullptr));SingletonThreadPool<Task>::GetInStance()->ThreadStart();int len = oper.size(); while(true){sleep(1);int data1 = rand() % 10;int data2 = rand() % 10 + 1;char op = oper[rand() % len];Task t(data1, data2, op);SingletonThreadPool<Task>::GetInStance()->push(t);t.GetTask();}return 0;
}

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

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

相关文章

【Android Compose】焦点管理

官方文档链接&#xff1a; https://developer.android.google.cn/develop/ui/compose/touch-input/focus?hlzh-cn 1、更改焦点遍历顺序 1.1、替换一维遍历顺序 &#xff08;1&#xff09;创建焦点引用对象&#xff1a; /// 创建4个引用对象&#xff08;二选一&#xff09…

dwj2025426

目录 一、25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#xff09; 二、 215. 数组中的第K个最大元素 - 力扣&#xff08;LeetCode&#xff09; 三、 15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 一、25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#…

C++ std::forward 详解

在 C 11 引入的众多特性中&#xff0c;std::forward占据着独特且重要的地位。它主要用于实现所谓的 “完美转发”&#xff0c;这一机制在现代 C 编程中发挥着关键作用&#xff0c;尤其是在编写通用库和高效代码时。 什么是完美转发&#xff1f; 完美转发是指在函数模板中&…

如何保证线程安全(含典型手段与应用场景)

✨ 1. 什么是线程安全&#xff1f; 线程安全指的是&#xff1a;当多个线程同时访问同一块代码时&#xff0c;无论运行时环境采用怎样的调度方式或者这些线程将怎样交替执行&#xff0c;代码的行为都能正确执行&#xff0c;且不会出现数据不一致、脏数据或异常崩溃。 举个简单…

Qt/C++开发监控GB28181系统/协议解释说明/SIP内容解释/每一行数据什么含义

一、前言 搞gb28181开发&#xff0c;首要任务就是解析协议&#xff0c;按照gb28181的文档来&#xff0c;还是非常详细的&#xff0c;通过抓包工具可以查看到具体的收发数据&#xff0c;也可以打开网络调试助手工具&#xff0c;监听5060端口&#xff0c;看到上报的数据&#xf…

C++:string 1

练习题&#xff1a; 这个题的思路是从前往后&#xff0c;从后往前同时找&#xff0c;不是字母的话就继续&#xff0c;是的话就交换。 代码&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include <string> using namespace std; //1、4个…

SMT贴片加工费控制与优化实践指南

内容概要 SMT贴片加工费的控制与优化需建立在对成本结构的系统性认知基础上。本节从物料采购、设备运行、工艺参数三大维度切入&#xff0c;结合BOM清单管理、钢网使用规范等实操环节&#xff0c;构建覆盖全流程的降本增效框架。以下表格列举了SMT加工成本的典型构成要素及其占…

未来医院已来:AI如何实现无死角安全监控

AI智慧医院如何用算法守护安全与效率 ## 背景&#xff1a;医疗场景的智能化转型需求 现代医院作为人员密集、场景复杂的公共场所&#xff0c;面临诸多管理痛点&#xff1a;患者跌倒可能延误救治、医闹事件威胁安全、医疗垃圾处置不当引发感染风险、重点区域&#xff08;如药…

Nuxt3中使用UnoCSS指南

Nuxt3中使用UnoCSS指南 UnoCSS是一个高度可定制的、原子化CSS引擎&#xff0c;可以轻松集成到Nuxt3项目中。下面介绍如何在Nuxt3中安装和配置UnoCSS。 安装步骤 安装UnoCSS的Nuxt模块&#xff1a; # 使用pnpm pnpm add -D unocss unocss/nuxt# 使用yarn yarn add -D unocss…

mmap详解

mmap详解 mmap基础概念mmap内存映射原理mmap相关函数调用mmap的使用细节mmap和常规文件操作的区别 mmap基础概念 mmap是一种内存映射文件的方法&#xff0c;即将一个文件或者其它对象映射到进程的地址空间&#xff0c;实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一…

Vue3的内置组件 -实现过渡动画 TransitionGroup

Vue3的内置组件 -实现过渡动画 TransitionGroup 是一个内置组件&#xff0c;用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果 支持和 基本相同的 props、CSS 过渡 class 和 JavaScript 钩子监听器&#xff0c;但有以下几点区别&#xff1a; 默认情况下&…

【软考-架构】14、软件可靠性基础

✨资料&文章更新✨ GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目录 软件可靠性基本概念软件可靠性建模软件可靠性管理软件可靠性设计N版本程序设计恢复块设计&#xff08;动态冗余&#xff09;双机容错技术、集群技术负载均衡软件可靠性测试…

使用Python+OpenCV对视频抽帧保存为JPG图像

使用PythonOpenCV对视频抽帧保存为JPG图像 import os import cv2 import time#视频文件夹路径&#xff0c;可修改 videoPath D:\\video\\ #保存的图片文件夹路径&#xff0c;可修改 savePath D:\\images\\ videolist os.listdir(videoPath) if not os.path.exists(savePath…

学习整理在centos7上安装mysql8.0版本教程

学习整理在centos7上安装mysql8.0版本教程 查看linux系统版本下载mysql数据库安装环境检查解压mysql安装包创建MySQL需要的目录及授权新增用户组新增组用户配置mysql环境变量编写MySQL配置文件初始化数据库初始化msyql服务启动mysql修改初始化密码配置Linux 系统服务工具,使My…

DeepSeek预训练追求极致的训练效率的做法

DeepSeek在预训练阶段通过多种技术手段实现了极致的训练效率,其中包括采用FP8混合精度训练框架以降低计算和内存需求 ,创新性地引入Multi-head Latent Attention(MLA)压缩KV缓存以提升推理效率,以及基于Mixture-of-Experts(MoE)的稀疏计算架构以在保证性能的同时显著降低…

【计算机视觉】CV项目实战- 深度解析TorchVision_Maskrcnn:基于PyTorch的实例分割实战指南

深度解析TorchVision_Maskrcnn&#xff1a;基于PyTorch的实例分割实战指南 技术背景与核心原理Mask R-CNN架构解析项目特点 完整实战流程环境准备硬件要求软件依赖 数据准备与标注1. 图像采集2. 数据标注3. 数据格式转换 模型构建与训练1. 模型初始化2. 数据加载器配置3. 训练优…

x86系列CPU寄存器和汇编指令总结

文章目录 概要一、寄存器1.1、8086寄存器1.2、通用寄存器1.3、扩展寄存器 二、指令集三、x86指令集常见指令使用说明四、汇编4.1、汇编语法4.2、nsam汇编 五、参考 概要 在对学习Go的过程中&#xff0c;涉及到了汇编&#xff0c;因此对X86系列CPU的背景、寄存器、汇编指令做了一…

戴维斯双击选股公式如何编写?

戴维斯双击&#xff0c;指的是营收增长和净利润增长同步&#xff0c;并有超预期的财务状况。 戴维斯双击是指在低市盈率&#xff08;P/E&#xff09;时买入股票&#xff0c;待公司盈利增长和市盈率提升后卖出&#xff0c;以获取双重收益。以下是一个简单的通达信选股模型示例&…

前端面试宝典---vue原理

vue的Observer简化版 class Observer {constructor(value) {if (!value || typeof value ! object) returnthis.walk(value) // 对对象的所有属性进行遍历并定义响应式}walk (obj) {Object.keys(obj).forEach(key > defineReactive(obj, key, obj[key]))} } // 定义核心方法…

从“聋哑设备“到超级工厂:EtherCAT转Modbus协议网关正在重构工业未来

当全球工厂加速迈向工业4.0&#xff0c;您的生产线是否因Modbus设备“拖后腿”而被迫降速&#xff1f;无需百万改造&#xff01;无需淘汰设备&#xff01;一套EtherCAT从站转Modbus协议网关&#xff0c;让30年老机床与智能工厂实时对话&#xff0c;效率飙升300%&#xff01; 一…