Linux简化版线程池

news2024/12/23 13:43:52

目录

一,线程池设计

二,线程池应用场景

三,线程池准备

1,包装一个锁

2,一个任务类

三,线程池

1,成员介绍

2,设计单例模式

3,创建线程池

4,线程池执行的任务

5,线程池取任务和加任务

6,整体代码实现

 四,线程池使用

五,谢谢各位佬的观看


一,线程池设计

        我们这个简单线程池设计思想就是提前创建好一批线程,在没有任务的时候就在条件变量下等待,有任务了就唤醒线程去执行任务,执行完了之后不要释放,在条件变量下等待执行任务就好了。准点就在这里,线程创建好了之后执完任务不释放,这大大提高了效率。

       我们把我们的main函数就当作一个创建任务的线程就好了,创建好了之后就不需要管了,唤醒线程池中等待的线程就好了,交给线程池去处理。线程池就不断的处理任务,处理完后继续等待。

二,线程池应用场景

        就目前理解的话,线程池对于那种处理时间短的任务是非常赚的,频繁的短任务,用线程池处理完之后那个线程马上返回线程池继续处理下一个任务,因此如果是许多很长的任务的话就不太适合使用线程池,直接用普通的线程去处理就好了。

三,线程池准备

1,包装一个锁

    包装一个生命周期随对象的锁,这里起始时练习一下智能指针的思想,可以完全不搞这一个的,但是我写了,就懒得在该回去了。其实也简单,就是在创建对象的时候在构造函数中加锁,对象处理作用域析构的时候我们在析构函数里面解锁。

#pragma once

#include <iostream>
class Mutex
{

public:
    Mutex()
    {
        pthread_mutex_init(&lock_, nullptr);
    }
    ~Mutex()
    {
        pthread_mutex_destroy(&lock_);
    }
    void lock()
    {
        pthread_mutex_lock(&lock_);
    }
    void unlock()
    {
        pthread_mutex_unlock(&lock_);
    }
private:
    pthread_mutex_t lock_;
};
// 让锁的生命周期最对象的生命周期。
class LockGuard
{
    public:
    // 构造对象的时候加锁。
    LockGuard(Mutex* mutex)
        : mutex_(mutex)
    {
        mutex_->lock();
    }
    ~LockGuard()
    {
        mutex_->unlock();
    }
private:
    Mutex *mutex_;
};

2,一个任务类

      不然创建任务就是插入数据,处理任务就是拿数据有点太单调了,我们这里任务类不写了,用上一节写的处理+-*/%的简答网络计算器。

#pragma once

#include <iostream>
#include <string>

// 一个任务类,并且他自己实现了处理任务的方法。
class Task
{
public:
    Task(int one = 1, int two = 1, char op = '*')
        : one_(one), two_(two), op_(op)
    {

    }
    // 析构不需要写。

    // 处理任务
    int run()
    {
        int resault = -1;
        switch (op_)
        {
        case '+':
            resault = one_ + two_;
            break;
        case '-':
            resault = one_ - two_;
            break;
        case '*':
            resault = one_ * two_;
            break;
        case '/':
            if (two_ == 0)
            {
                resault = -1;
                flag = false;
            }
            else
            {
                resault = one_ / two_;
            }
            break;
        case '%':
            if (two_ == 0)
            {
                resault = -1;
                flag = false;
            }
            else
            {
                resault = one_ % two_;
            }
            break;
        default:
            flag = false;
            resault = -1;
            break;
        }
        return resault;
    }

    // 用算符重载。
    int operator() ()
    {
        return run();
    }

    // 把需要处理的任务用输出型参数带回,方便打印。
    void getdata(int *one, int *two, char *op)
    {
        *one = one_;
        *two = two_;
        *op = op_;
    }
    bool istrue()
    {
        return flag;
    }
private:
    int one_;
    int two_;
    char op_;
    bool flag = true;
};

三,线程池

1,成员介绍

      我们需要一个互斥锁来保护临界资源。

      一个条件变量在没有任务的时候让线程等待。

     一个队列来保存任务。

     一个标记位来确保线程不会应为多次启动线程池而创建多过的线程。

     一个整形表示线程池中线程数量。

     一个自己的static 指针,我们把线程池搞成单例模式。

2,设计单例模式

构造函数私有:

禁掉拷贝构造,赋值重载

留下一个接口来创建对象,

       这个接口只会在第一次调用的时候创建一个对象,之后的调用只会返回这个对象的地址。

3,创建线程池

 

4,线程池执行的任务

5,线程池取任务和加任务

6,整体代码实现

#pragma once
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <queue>
#include <cassert>
#include "Lock.hpp"
#include"Task.hpp"
using namespace std;

template <class T>
class ThreadPool
{

public:

    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }

    // 准备搞成单例模式的。
    ThreadPool(const ThreadPool<T> &in) = delete;
    void operator=(const ThreadPool<T> &in) = delete;

    // 单例模式一般通过一个接口获取对象,第一次获取是创建,之后直接返回对象的指针。
    static ThreadPool<T> *getinstance()
    {
        static Mutex mutex;
        if (instance_ == nullptr)
        {
            // 构建对象是自动加锁。
            LockGuard LockGuard(&mutex);

            if (instance_ == nullptr)
            {
                instance_ = new ThreadPool<T>();
            }
        }
        // 处理作用域自动解锁。
        return instance_;
    }


//这里要传自己的参数,成员函数内定义线程函数一定要加 static;
    static void *threadrooting(void* args)
    {
        pthread_detach(pthread_self());

        ThreadPool<T> *dp = (ThreadPool<T>*)args;

        while (true)
        {
             dp->LockQueue();

            while (!dp->HaveTask())
            {
                dp->WaitForTask();
            }

            // 走到s这里,有任务的。
            T tmp = dp->pop();
            dp->unLockQueue();
         int one;
         int two;
         char op;
         tmp.getdata(&one,&two,&op);
         cout<<"线程:"<<pthread_self()<<"执行任务:"<<one<<op<<two<<"="<<tmp()<<endl;
        }
    }

    void start()
    {
        assert(!isstart_);
        for (int i = 0; i < threadnum_; i++)
        {
            pthread_t t1;
            pthread_create(&t1, nullptr, threadrooting,this);
        }
    }

    // 这里不需要加锁了,因为线程处理方法那里加过锁了。注意了不要加不必要的锁影响效率。
    T pop()
    {
        T tmp = Taskqueue_.front();
        Taskqueue_.pop();
        return tmp;
    }

    void push(const T& in)
    {
        LockQueue();
        Taskqueue_.push(in);
        unLockQueue();

        //唤醒一个线程去处理这个任务。
        AwakenForThread();
    }

private:
    ThreadPool(int threadnum = 5)
        : threadnum_(threadnum), isstart_(false)
    {
        assert(threadnum > 0);
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }
    void LockQueue()
    {
        pthread_mutex_lock(&mutex_);
    }
    void unLockQueue()
    {
        pthread_mutex_unlock(&mutex_);
    }
    void WaitForTask()
    {
        //注意条件变量一定要加锁了,不然就完犊子了。
        pthread_cond_wait(&cond_, &mutex_);
    }
    void AwakenForThread()
    {
        pthread_cond_signal(&cond_);
    }
    bool HaveTask()
    {
        return !Taskqueue_.empty();
    }

private:
   
    bool isstart_; // 标记第一次创建线程池。
    queue<T> Taskqueue_;//保存任务
    int threadnum_;//线程池线程个数
    pthread_mutex_t mutex_;//互斥锁
    pthread_cond_t cond_;//条件变量

    // 搞一个单例模式。
    static ThreadPool<T> *instance_;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::instance_ = nullptr;

 四,线程池使用

        我们main函数作为一个创建任务的线程就好了,创建好了就加入线程池,线程池自己就执行任务了。

#include <iostream>
#include <ctime>
#include <string>
#include<thread>
#include "Task.hpp"
#include "ThreadPool.hpp"

using namespace std;
string ops = "+-*/%";
int main()
{

    ThreadPool<Task>* dp;
    dp=ThreadPool<Task>::getinstance();
//unique_ptr<ThreadPool<Task> > dp(ThreadPool<Task>::getinstance());
    //启动线程池。
    dp->start();
    // 主进程相当于有一个派发任务的就好了,只管派发任务,人下的不需要管了。

    while (true)
    {
        // 构建一个任务、
        int one = rand() % 50;
        int two = rand() % 50;
        char op = ops[rand() % ops.size()];

        Task t = Task(one, two, op);

        cout << "生产了一个任务:"<<one<<op<<two<<"=?"<<endl;
              dp->push(t);
        sleep(1);
    }

    return 0;
}

 结果展示

五,谢谢各位佬的观看

      感谢各位佬观看,必须单独一章!!!。

 

 

 

 

 

 

 

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

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

相关文章

【吴恩达机器学习笔记】十六、应用实例:图片文字识别

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4e3;专栏定位&#xff1a;为学习吴恩达机器学习视频的同学提供的随堂笔记。 &#x1f4da;专栏简介&#xff1a;在这个专栏&#xff0c;我将整理吴恩达机器学习视频的所有内容的笔记&…

【Linux】Zabbix5.0平台的搭建

文章目录项目背景视频展演一、Linux基础配置1、查看当前系统版本2、修改主机名3、修改 IP 地址4、配置防火墙5、关闭 SELINUX6、修改系统时间及时区7、配置 YUM 库方式 1方式 2二、安装apache1、安装 apache2、启动 apache 服务3、设置 httpd 服务开机启动4、查看服务状态5、防…

深度学习——制作自己的VOC图像分割数据集

1、数据集介绍 COCO数据集有80个类别,VOC数据集有20个类别。当这些数据集类别中没有自己需要的时候,就需要自己动手做自己的数据集了。 我自己在做数据集的时候主要使用到了labelme和labelImg两个工具。labelme主要是制作语义分割数据集(ImageSets,JPEGImages,SegmentationC…

kubernetes 1.18 部署 ingress-nginx

文章目录kubernetes 1.18 部署 ingress-nginx1. 下载 yaml 文件2. 安装 ingress-nginx3. 检查安装情况4. 测试验证4.1 查看ingress规则4.2 访问测试5. 其他内容kubernetes 1.18 部署 ingress-nginx 1. 下载 yaml 文件 在 GitHub 下载完成之后可以直接使用&#xff0c;不需要修…

[基因遗传算法]进阶之三:sko.GA的实践TSP

参考资料:《VRP问题分类》 相关文章: 《[基因遗传算法]原理思想和python代码的结合理解之(一) :单变量》 《[基因遗传算法]进阶之二:最优规划问题–多种编码方式多变量》 文章目录一. GA的用法1.1 help(sko.GA)1.2 目标函数的书写A. 单变量的书写B. 多变量的书写C. 变量的范围…

mysql中的B+树、索引跳跃扫描

普通索引 B树的叶子节点上记录的是聚簇索引&#xff08;主键索引&#xff09;的值。 联合索引 叶子节点中记录的是name&#xff0c;age两个字段以及主键id的值。 MySQL一定是遵循最左前缀匹配的&#xff0c;这句话在mysql8以前是正确的&#xff0c;没有任何毛病。但是在M…

(文章复现)7.计及电转气协同的含碳捕集与垃圾焚烧虚拟电厂优化调度(MATLAB程序)

联系方式&#xff1a;2645521500 复现文章&#xff1a; 计及电转气协同的含碳捕集与垃圾焚烧虚拟电厂优化调度——孙惠娟&#xff08;电网技术—2020&#xff09; 摘要&#xff1a; 为了促进多能源互补及能源低碳化&#xff0c;本文提出了计及电转气协同的含碳捕集与垃圾焚…

有手就会做,保姆级Jmeter分布式压测操作流程(图文并茂)

分布式压测原理 分布式压测操作 保证本机和执行机的JDK和Jmeter版本一致 配置Jmeter环境变量 配置Jmeter配置文件 上传每个执行机服务jmeter chmod -R 755 apache-jmeter-5.1.1/ 执行机配置写自己的ip 控制机配置所有执行机ip,把server.rmi.ssl.disable改成true 将本机也作…

Java-1208

JVM与Java体系结构 JVM整体结构&#xff08;上图主要针对hotspot虚拟机&#xff09; 类加载器&#xff1a; 将字节码文件加载进去&#xff0c;并不一定是java字节码文件&#xff0c;很多语言都会编译成字节码文件使用JVM 运行时数据区&#xff1a; 方法区和堆&#xff1a;使用了…

毕业设计 STM32单片机智能WiFi天气助手 - 物联网 单片机

文章目录0 前言1 设计内容2 软件设计3 关键代码4 最后0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的项目系统达不…

Go设计与实现--数组与切片

数组 初始化 Go语言数组的初始化是在编译期就已经执行好了。这个是初始化的代码&#xff1a; // NewArray returns a new fixed-length array Type. func NewArray(elem *Type, bound int64) *Type {if bound < 0 {base.Fatalf("NewArray: invalid bound %v", …

Security实现前后端分离

Security实现前后端分离 说明 ​ 上一篇和上上一篇我大致介绍了一下security基础使用和oauth2的一些流程&#xff0c;这里在深入了解一些相关的配置项。 ​ 首先我们在梳理一下相关概念&#xff0c;首先基本的security是负责用户认证这这一环节&#xff0c;总而言之就是用户…

PCB入门学习—原理图的绘制1(MCU部分)

目录 2.1 STM32F103VET6 MCU核心电路的绘制 学习目录 2.1 STM32F103VET6 MCU核心电路的绘制 总结&#xff1a;放置元件&#xff0c;连线&#xff0c;放置网络标号&#xff0c;更新序号。 主控放上去之后原理图图纸不太够&#xff1a;双击右边边缘&#xff0c;默认图纸大小是…

【Java小案例】从简到精完美判断年份是闰/平年和该年二月份有几天

目录前言问题描述思路分析解决方案方案一方案二方案三方案四结语前言 1、平年指阳历没有闰日或农历没有闰月的年份&#xff0c;闰年是公历中的名词&#xff0c;是为了弥补因人为历法规定造成的年度天数与地球实际公转周期的时间差而设立的&#xff0c;补上时间差的年份为闰年&a…

QA | SWCF2022 笔记:GNSS模拟赋能汽车HIL测试

2022年度SWCF卫星通信与仿真测试研讨会正在进行中&#xff0c;精彩演讲&#xff1a;GNSS模拟赋能汽车HIL测试&#xff0c;感谢大家的观看与支持&#xff01;收到一些粉丝的技术问题&#xff0c;我们汇总了热点问题并请讲师详细解答&#xff0c;在此整理分享给大家&#xff01; …

高通平台开发系列讲解(UART篇)高速串口代码流程

文章目录 一、初始化1.1、Registration with the SPS driver1.2、UART port registration二、Port open三、Port close沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要介绍高通平台高速串口代码流程。 一、初始化 初始化流程: msm_serial_hs_init() ->

数据传送指令MOV、XCHG

学习过程中要重点掌握对标志寄存器的影响 数据传送类指令&#xff08;不影响标志位&#xff09; 一&#xff1a;MOV指令 先要知道图片中这几个英文表示什么 立即数&#xff08;immediaate operand&#xff09; 寄存器&#xff08;register&#xff09; 内存&#xff08;…

WEB前端大作业HTML静态网页设计旅游景点区主题——三亚旅游网页设计

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

基于Java+Swing实现超级玛丽游戏

基于JavaSwing实现超级玛丽游戏一、系统介绍二、功能展示三、其他系统一、系统介绍 超级玛丽小游戏的JAVA程序&#xff0c;进入游戏后首先按空格键开始&#xff0c;利用方向键来控制的马里奥的移动&#xff0c;同时检测马里奥与场景中的障碍物和敌人的碰撞&#xff0c;并判断马…

JVM八股文,面试会被问到什么?都在这里啦 ~

目录 1、JVM内存划分 1.1、程序计数器&#xff08;Program Counter Register&#xff09; 1.2、方法区&#xff08;Method Area&#xff09; 1.3、本地方法栈&#xff08;Native Method Stacks&#xff09; 1.4、虚拟机栈&#xff08;JVM Stacks&#xff09; 1.5、Java堆…