【Linux】线程池

目录

  • 一、线程池
    • 1.什么是线程池
    • 2.线程池图解
    • 3.实现代码
  • 二、单例模式
    • 1.单例模式的概念
    • 2.饿汉方式实现单例模式
    • 3.懒汉方式实现单例模式
    • 4.懒汉方式实现单例模式的线程池

一、线程池

1.什么是线程池

线程虽然比进程轻量了很多,但是每创建一个线程时,需要向操作系统申请空间创建,如果需要开辟大量的线程,申请和销毁的开销也是很大的。所以如果能够提前申请一块空间,专门用来创建线程,那么就能提高一些效率。

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

线程池通过一个线程安全的阻塞任务队列加上一个或一个以上的线程实现,线程池中的线程可以从阻塞队列中获取任务进行任务处理,当线程都处于繁忙状态时可以将任务加入阻塞队列中,等到其它的线程空闲后进行处理。

线程池的作用:可以避免大量线程频繁创建或销毁所带来的时间成本,也可以避免在峰值压力下,系统资源耗尽的风险;并且可以统一对线程池中的线程进行管理,调度监控。

线程池的应用场景:

  • 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,可以想象一个热门网站的点击次数。

  • 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

  • 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,导致出现错误。

2.线程池图解

在这里插入图片描述

3.实现代码

Task.cpp 任务对象

#pragma once
#include <iostream>
#include <string>
#include <unistd.h>class Task { public:Task(){}Task(int x, int y, char op) : _x(x), _y(y), _op(op), _result(0), _exitCode(0){}void operator()(){switch (_op){case '+':_result = _x + _y;break;case '-':_result = _x - _y;break;case '*':_result = _x * _y;break;case '/':{if (_y == 0)_exitCode = -1;else_result = _x / _y;}break;case '%':{if (_y == 0)_exitCode = -2;else_result = _x % _y;}break;default:break;}usleep(100000);}std::string formatArg(){return std::to_string(_x) + _op + std::to_string(_y) + "= ?";}std::string formatRes(){return std::to_string(_result) + "(" + std::to_string(_exitCode) + ")";}~Task(){}private:int _x;int _y;char _op;int _result;int _exitCode; }; ```

ThreadPool_v1.hpp 自定义实现线程池

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include "Task.hpp"const static int N = 5;template <class T> class ThreadPool { public:ThreadPool(int num = N) : _num(num), _threads(num){pthread_mutex_init(&_lock, nullptr);pthread_cond_init(&_cond, nullptr);}void lockQueue(){pthread_mutex_lock(&_lock);}void unlockQueue(){pthread_mutex_unlock(&_lock);}void threadWait(){pthread_cond_wait(&_cond, &_lock);}void threadWakeup(){pthread_cond_signal(&_cond);}bool isEmpty(){return _tasks.empty();}T popTask(){T t = _tasks.front();_tasks.pop();return t;}static void *threadRoutine(void *args){pthread_detach(pthread_self());ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);while (true){// 1. 检测有没有任务// 2. 有:处理// 3. 无:等待// 细节:必定加锁tp->lockQueue();while (tp->isEmpty()){tp->threadWait();}T t = tp->popTask(); // 从公共区域拿到私有区域tp->unlockQueue();// for testt();std::cout << "thread handler done, result: " << t.formatRes() << std::endl;// t.run(); // 处理任务,不应该在临界区中处理 ,已经拿到任务,线程可以自己去处理}}void init(){// TODO}void start(){for (int i = 0; i < _num; i++){pthread_create(&_threads[i], nullptr, threadRoutine, (void *)this); // ?}}void pushTask(const T &t){lockQueue();_tasks.push(t);threadWakeup();unlockQueue();}~ThreadPool(){pthread_mutex_destroy(&_lock);pthread_cond_destroy(&_cond);}private:std::vector<pthread_t> _threads;int _num;std::queue<T> _tasks; // 使用stl的自动扩容的特性pthread_mutex_t _lock;pthread_cond_t _cond; }; ```

main.cpp

#include "ThreadPool_v1.hpp"
#include "Task.hpp"
#include <memory>int main() {ThreadPool<Task> tp;tp.init();tp.start();while (true){int x, y;char op;std::cout << "please Enter x> ";std::cin >> x;std::cout << "please Enter y> ";std::cin >> y;std::cout << "please Enter op(+-*/%)> ";std::cin >> op;Task t(x, y, op);//ThreadPool<Task>::getinstance()->pushTask(t);tp.pushTask(t);} } ```

二、单例模式

1.单例模式的概念

一个类,只应该实例化出一个对象,就称为单例。

定义对象的本质,是将对象加载到内存,只让该对象在内存中加载一次,就是单例。

对象被设计成单例的场景:①语义上只需要一个对象 ②该对象内部存在大量的空间,保存了大量的数据,如果允许该对象存在多份,或者允许发生拷贝,内存中会存在数据冗余。

单例模式的两种模式: 饿汉模式、懒汉模式。

懒汉模式:对于一个进程来说,我们只有在第一次使用的时候才去创建。优点是节省进程加载的时间。缺点是在第一次使用的时候才去创建。

饿汉模式:在代码转化为进程时,先于main函数之前就已经创建好了,不需要使用的时候再去创建。优点是可以直接使用,该资源早早创建出来,影响进程加载的速度。

2.饿汉方式实现单例模式

template <class T>
class Singleton 
{static T data; // 静态成员,该成员属于类,不属于对象,一旦创建了类,该成员就被创建了
public:static T* GetInstance() {return &data;}};
template <class T>
T Singleton<T>:: data = T();

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

3.懒汉方式实现单例模式

template <class T>
class Singleton 
{static T* inst; // 先创建静态成员的指针,指针指向空
public:static T* GetInstance(){if (inst == nullptr) {inst = new T(); // 一旦调用了该函数(即需要用到该对象的时候),才创建该对象} return inst;}
};
template <class T>
T* Singleton<T>:: inst = nullptr;

存在线程安全问题: 在没有实例化对象时, 如果两个线程同时调用GetInstance, 可能会创建出两份 T 对象的实例。所以需在创建单例时进行加锁。

4.懒汉方式实现单例模式的线程池

其中的大概框架还是线程池,只是创建对象的方式改变了。
不会在main函数中一开就创建对象,而是在获取到任务之后才创建。并且实现了线程安全。
我们还将前面自己封装的线程类和自己封装的锁加入线程池的使用

Task.hpp 对象类,模拟一个任务

#pragma once
#include <iostream>
#include <string>
#include <unistd.h>class Task { public:Task(){}Task(int x, int y, char op) : _x(x), _y(y), _op(op), _result(0), _exitCode(0){}void operator()(){switch (_op){case '+':_result = _x + _y;break;case '-':_result = _x - _y;break;case '*':_result = _x * _y;break;case '/':{if (_y == 0)_exitCode = -1;else_result = _x / _y;}break;case '%':{if (_y == 0)_exitCode = -2;else_result = _x % _y;}break;default:break;}usleep(100000);}std::string formatArg(){return std::to_string(_x) + _op + std::to_string(_y) + "= ?";}std::string formatRes(){return std::to_string(_result) + "(" + std::to_string(_exitCode) + ")";}~Task(){}private:int _x;int _y;char _op;int _result;int _exitCode; }; ```

lockGuard.hpp 自定义封装的锁

#pragma once#include <iostream>
#include <pthread.h>class Mutex // 自己不维护锁,有外部传入 { public:Mutex(pthread_mutex_t *mutex):_pmutex(mutex){}void lock(){pthread_mutex_lock(_pmutex);}void unlock(){pthread_mutex_unlock(_pmutex);}~Mutex(){} private:pthread_mutex_t *_pmutex; };class LockGuard // 自己不维护锁,有外部传入 { public:LockGuard(pthread_mutex_t *mutex):_mutex(mutex){_mutex.lock();}~LockGuard(){_mutex.unlock();} private:Mutex _mutex; };

Thread.hpp 自定义封装的线程

#pragma once#include <iostream>
#include <string>
#include<stdio.h>
#include <cstdlib>
#include<pthread.h>class Thread { public:typedef enum{NEW = 0,RUNNING,EXITED} ThreadStatus;typedef void (*func_t)(void *);public:Thread(int num, func_t func, void *args) : _tid(0), _status(NEW), _func(func), _args(args){char name[128];snprintf(name, sizeof(name), "thread-%d", num);_name = name;}int status() { return _status; }std::string threadname() { return _name; }pthread_t threadid(){if (_status == RUNNING)return _tid;else{return 0;}}// 是不是类的成员函数,而类的成员函数,具有默认参数this,需要static// 但是会有新的问题:static成员函数,无法直接访问类属性和其他成员函数static void *runHelper(void *args){Thread *ts = (Thread*)args; //拿到当前对象// _func(_args);(*ts)();return nullptr;}void operator ()() //仿函数{if(_func != nullptr) _func(_args);}void run(){int n = pthread_create(&_tid, nullptr, runHelper, this);if(n != 0) exit(1);_status = RUNNING;}void join(){int n = pthread_join(_tid, nullptr);if( n!=0){std::cerr << "main thread join thread " << _name << " error" << std::endl;return;}_status = EXITED;}~Thread(){}private:pthread_t _tid;std::string _name;func_t _func; // 线程未来要执行的回调void *_args;ThreadStatus _status; }; ``

ThreadPool_v2.hpp 饿汉方式实现单例模式的线程池

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <unistd.h>
#include "Thread.hpp"
#include "Task.hpp"
#include "lockGuard.hpp"const static int N = 5;template <class T> class ThreadPool { private:ThreadPool(int num = N) : _num(num){pthread_mutex_init(&_lock, nullptr);pthread_cond_init(&_cond, nullptr);}ThreadPool(const ThreadPool<T> &tp) = delete;void operator=(const ThreadPool<T> &tp) = delete;public:static ThreadPool<T> *getinstance(){if(nullptr == instance) // 提高效率,减少加锁的次数{LockGuard lockguard(&instance_lock);if (nullptr == instance){instance = new ThreadPool<T>();instance->init();instance->start();}}return instance;}pthread_mutex_t *getlock(){return &_lock;}void threadWait(){pthread_cond_wait(&_cond, &_lock);}void threadWakeup(){pthread_cond_signal(&_cond);}bool isEmpty(){return _tasks.empty();}T popTask(){T t = _tasks.front();_tasks.pop();return t;}static void threadRoutine(void *args){// pthread_detach(pthread_self());ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);while (true){// 1. 检测有没有任务// 2. 有:处理// 3. 无:等待// 细节:必定加锁T t;{LockGuard lockguard(tp->getlock());while (tp->isEmpty()){tp->threadWait();}t = tp->popTask(); // 从公共区域拿到私有区域}t();std::cout << "thread handler done, result: " << t.formatRes() << std::endl;// t.run(); // 处理对象在临界区外}}void init(){for (int i = 0; i < _num; i++){_threads.push_back(Thread(i, threadRoutine, this));}}void start(){for (auto &t : _threads){t.run();}}void check(){for (auto &t : _threads){std::cout << t.threadname() << " running..." << std::endl;}}void pushTask(const T &t){LockGuard lockgrard(&_lock);_tasks.push(t);threadWakeup();}~ThreadPool(){for (auto &t : _threads){t.join();}pthread_mutex_destroy(&_lock);pthread_cond_destroy(&_cond);}private:std::vector<Thread> _threads;int _num;std::queue<T> _tasks; // 使用stl的自动扩容的特性pthread_mutex_t _lock;pthread_cond_t _cond;static ThreadPool<T> *instance;static pthread_mutex_t instance_lock; };template <class T> ThreadPool<T> *ThreadPool<T>::instance = nullptr;template <class T> pthread_mutex_t ThreadPool<T>::instance_lock =
PTHREAD_MUTEX_INITIALIZER;

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

UCOS的任务创建和删除

一、任务创建和删除的API函数 1、任务创建和删除本质就是调用uC/OS的函数 API函数 描述 OSTaskCreate() 创建任务 OSTaskDel() 删除任务 注意&#xff1a; 1&#xff0c;使用OSTaskCreate() 创建任务&#xff0c;任务的任务控制块以及任务栈空间所需的内存&#xff0c…

算法——买卖股票问题

309. 买卖股票的最佳时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; 一、 究其就是个动态规划的问题 算法实现图 初始化 由于有三个阶段&#xff0c;买入&#xff0c;可交易&#xff0c;冷冻期&#xff0c;那么用dp表表示现在为止的最大利润&#xff0c;则有 dp[0][…

asp.net core 远程调试

大概说下过程&#xff1a; 1、站点发布使用Debug模式 2、拷贝到远程服务器&#xff0c;以及iis创建站点。 3、本地的VS2022的安装目录&#xff1a;C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE下找Remote Debugger 你的服务器是64位就拷贝x64的目…

详解Linux的系统调用fork()函数

在Linux系统中&#xff0c;fork()是一个非常重要的系统调用&#xff0c;它的作用是创建一个新的进程。具体来说&#xff0c;fork()函数会在当前进程的地址空间中复制一份子进程&#xff0c;并且这个子进程几乎完全与父进程相同&#xff0c;包括进程代码、数据、堆栈以及打开的文…

WebSocket实战之四WSS配置

一、前言 上一篇文章WebSocket实战之三遇上PAC &#xff0c;碰到的问题只能上安全的WebSocket&#xff08;WSS&#xff09;才能解决&#xff0c;配置证书还是挺麻烦的&#xff0c;主要是每年都需要重新更新证书&#xff0c;我配置过的证书最长有效期也只有两年&#xff0c;搞不…

ElasticSearch第四讲:ES详解:ElasticSearch和Kibana安装

ElasticSearch第四讲&#xff1a;ES详解&#xff1a;ElasticSearch和Kibana安装 本文是ElasticSearch第四讲&#xff1a;ElasticSearch和Kibana安装&#xff0c;主要介绍ElasticSearch和Kibana的安装。了解完ElasticSearch基础和Elastic Stack生态后&#xff0c;我们便可以开始…

ctfshow—1024系列练习

1024 柏拉图 有点像rce远程执行&#xff0c;有四个按钮&#xff0c;分别对应四份php文件&#xff0c;开始搞一下。一开始&#xff0c;先要试探出 文件上传到哪里&#xff1f; 怎么读取上传的文件&#xff1f; 第一步&#xff1a;试探上传文件位置 直接用burp抓包&#xff0c;…

力扣练习——链表在线OJ

目录 提示&#xff1a; 一、移除链表元素 题目&#xff1a; 解答&#xff1a; 二、反转链表 题目&#xff1a; 解答&#xff1a; 三、找到链表的中间结点 题目&#xff1a; 解答&#xff1a; 四、合并两个有序链表&#xff08;经典&#xff09; 题目&#xff1a; 解…

【数据结构---排序】很详细的哦

本篇文章介绍数据结构中的几种排序哦~ 文章目录 前言一、排序是什么&#xff1f;二、排序的分类 1.直接插入排序2.希尔排序3.选择排序4.冒泡排序5.快速排序6.归并排序总结 前言 排序在我们的生活当中无处不在&#xff0c;当然&#xff0c;它在计算机程序当中也是一种很重要的操…

聊聊常见的IO模型 BIO/NIO/AIO 、DIO、多路复用等IO模型

聊聊常见的IO模型 BIO/NIO/AIO/DIO、IO多路复用等IO模型 文章目录 一、前言1. 什么是IO模型2. 为什么需要IO模型 二、常见的IO模型1. 同步阻塞IO&#xff08;Blocking IO&#xff0c;BIO&#xff09;2. 同步非阻塞IO&#xff08;Non-blocking IO&#xff0c;NIO&#xff09;3.…

排序算法之【希尔排序】

&#x1f4d9;作者简介&#xff1a; 清水加冰&#xff0c;目前大二在读&#xff0c;正在学习C/C、Python、操作系统、数据库等。 &#x1f4d8;相关专栏&#xff1a;C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 &#x1f44d…

八大排序源码(含优化)

文章目录 1、直接插入排序2、希尔排序3、选择排序4、冒泡排序5、堆排序6、快速排序快速排序递归实现霍尔法挖坑法前后指针法快速排序小区间优化 快速排序非递归实现 7、归并排序归并排序递归实现归并排序非递归 8、计数排序 大家好&#xff0c;我是纪宁&#xff0c;这篇文章是关…

java Spring Boot 自动启动热部署 (别再改点东西就要重启啦)

上文 java Spring Boot 手动启动热部署 我们实现了一个手动热部署的代码 但其实很多人会觉得 这叫说明热开发呀 这么捞 写完还要手动去点一下 很不友好 其实我们开发人员肯定是希望重启这种事不需要自己手动去做 那么 当然可以 我们就让它自己去做 Build Project 这个操作 我们…

Linux性能优化--性能工具:系统内存

3.0.概述 本章概述了系统级的Linux内存性能工具。本章将讨论这些工具可以测量的内存统计信息&#xff0c;以及如何使用各种工具收集这些统计结果。阅读本章后&#xff0c;你将能够&#xff1a; 理解系统级性能的基本指标&#xff0c;包括内存的使用情况。明白哪些工具可以检索…

Java21 新特性

文章目录 1. 概述2. JDK21 安装与配置3. 新特性3.1 switch模式匹配3.2 字符串模板3.3 顺序集合3.4 记录模式&#xff08;Record Patterns&#xff09;3.5 未命名类和实例的main方法&#xff08;预览版&#xff09;3.6 虚拟线程 1. 概述 2023年9月19日 &#xff0c;Oracle 发布了…

电子计算机核心发展(继电器-真空管-晶体管)

目录 继电器 最大的机电计算机之一——哈弗Mark1号&#xff0c;IBM1944年 背景 组成 性能 核心——继电器 简介 缺点 速度 齿轮磨损 Bug的由来 真空管诞生 组成 控制开关电流 继电器对比 磨损 速度 缺点 影响 代表 第一个可编程计算机 第一个真正通用&am…

使用晶体管做布尔逻辑和逻辑门

目录 二进制&#xff0c;三进制&#xff0c;五进制 true&#xff0c;false表示0&#xff0c;1 早期计算机采用进制 布尔逻辑 三个基本操作&#xff1a;NOT,AND,OR 基础“真值表” NOT 如何实现&#xff1f; AND如何实现&#xff1f; OR如何实现&#xff1f; 图标表示…

LLM之Colossal-LLaMA-2:Colossal-LLaMA-2的简介、安装、使用方法之详细攻略

LLM之Colossal-LLaMA-2&#xff1a;Colossal-LLaMA-2的简介、安装、使用方法之详细攻略 导读&#xff1a;2023年9月25日&#xff0c;Colossal-AI团队推出了开源模型Colossal-LLaMA-2-7B-base。Colossal-LLaMA-2项目的技术细节&#xff0c;主要核心要点总结如下: >> 数据处…

数据分析方法:RFM模型

一、RFM基本原理 RFM是三个单词的缩写&#xff1a; 最近一次消费时间&#xff08;Recency&#xff09;&#xff0c;取数的时候一般取最近一次消费记录到当前时间的间隔&#xff0c;比如&#xff1a;7天、30天、90天未到店消费&#xff1b;直观上&#xff0c;一个用户太久不到…

【计算机组成原理】考研真题攻克与重点知识点剖析 - 第 1 篇:计算机系统概述

前言 本文基础知识部分来自于b站&#xff1a;分享笔记的好人儿的思维导图&#xff0c;感谢大佬的开源精神&#xff0c;习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析&#xff0c;本人技术有限&#xff…