【Linux系统编程三十】线程池实现

线程池实现

  • 一.线程池的本质
  • 二.类内创建线程
  • 三.代码实现

一.线程池的本质

线程池里面存储的都是一批已经创建好的线程,当线程池里有数据时,这批线程就会被唤醒去竞争数据,当线程池里没有数据时,这批线程就去休眠等待。

线程池的本质就是一个生产消费模型,当有生产者线程往线程池里发送任务时,线程池里的消费者线程就会竞争任务。

比如主线程往线程池里投递一个任务,线程池里的若干线程就会被立刻唤醒,然后去竞争抢任务执行。
所以一开始线程池里的线程必须早就被创建出来的,只不过线程池里没有东西,它们都去条件变量下等待了。

所以如果要实现一个线程池,那么需要些什么呢?

1.首先线程池肯定需要存储任务数据的场所,我们可以用队列来存储。也就是线程池里需要一个任务队列,用来存储接收到的任务。
2.其次线程池里存储很多已经被创建的线程,所以里面还需要能够找到这个已经被创建的线程,如何找到呢?根据线程的tid,所以我们可以用vector数组存储线程的tid,来找到所有线程。
3.然后就是我们要保证生产消费过程中的安全,比如消费者只能互斥竞争任务,不能同时竞争任务,生产者在生产,消费者不能去消费等,所以需要加锁保护。
4.最后就是要保证,生产消费之间的有序性,线程池里线程不能一直去竞争任务,如果线程池里没有任务了,那么就需要到条件变量下等待。

二.类内创建线程

在实现的过程中,有一个细节,那就是在类内部创建线程,因为线程池中的线程是早就被创建好的,所以程序一开始运行时就需要存在,只不过这时线程池里的线程都在休眠。但是在创建线程时,需要传递线程的执行函数,线程的执行函数有特定的形式,必须要返回值是void类型,参数也是void类型。
当在类内部创建时,这个线程执行函数默认是类成员函数,成员函数的参数里默认会有一个this指针。
也就是在类内创建线程时,该线程执行函数的参数是有两个的,不符合要求。就会创建失败。
在这里插入图片描述

那该如何解决呢?
我们可以在成员函数的前面加上static,就变成了静态成员函数了,静态成员函数是没有this指针的。

但设置成静态成员函数后,又会存在一个问题,那就是该静态成员函数是无法直接访问成员变量的。而线程的执行函数是需要去到线程池里的任务队列里去竞争任务的,所以必须要访问类成员变量。在这里插入图片描述

【解决方法】
我们可以在创建线程时,将该类的this指针传给线程函数,这样线程的执行函数,就可以通过类型转换访问到类的成员变量了。
在这里插入图片描述

三.代码实现

#pragma once
#include <pthread.h>
#include <iostream>
#include <queue>
#include <string>
#include <vector>
//线程池的本质就是生产消费模型
//一个生产者往线程池里放任务,然后其他消费者者就竞争这个任务执行
//线程池里有很多线程,所以它的基本属性肯定有识别线程的id


static const int defaultnum=3;//默认线程池里有3个线程
struct ThreadInfo
{
   pthread_t tid;
   std::string name;
};
template <class T>
class ThreadPool
{
public:

    void Lock()
    {
        pthread_mutex_lock(&_mutex);
    }
    void Unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    void Makeup()
    {
        pthread_cond_signal(&_cond);
    }
    bool isQueueEmpty()
    {
        return _task.empty();
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&_cond,&_mutex);
    }
    T Pop()
    {
       T t=_task.front();
       _task.pop();
       return t;
    }
   
   std::string GetthreadName(pthread_t id)
   {
      for(const auto&ti :_thread)
      {
        if(ti.tid==id)
        return ti.name;
      }
      return "None";
   }
public:
     ThreadPool(int num=defaultnum):_thread(num)
     {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_cond,nullptr);
     }
     void Push(const T& in)//往线程池里发送任务,发送是没有条件的,但一旦发送了,就说明消费条件满足了,就要唤醒线程池里的线程去执行
     {
        Lock();
        _task.push(in);
        Makeup();//唤醒在条件变量下等待的线程
        Unlock();
     }

    
    
    //要注意,在类内部创建线程时,线程执行的函数里,会有this指针,不满足要求,所以必须要使用静态成员函数
    //这样才可以没有this指针,只有一个参数,但静态成员函数又不能访问类成员,所以在给线程函数传递参数时,我们传
    //该类的this指针,这样就可以通过this指针访问类成员
    static void *Handler(void* args)//去线程池里的任务队列里竞争任务
    {
      ThreadPool<T>* td=static_cast< ThreadPool<T>*>(args);
      std::string name=td->GetthreadName(pthread_self());//根据tid来获取到对应的名字
      //线程创建出来就去竞争任务,任务在哪里?在任务队列里,任务队列里没有怎么办?去条件变量下等待
      while(true)
      {
        td->Lock();

        while(td->isQueueEmpty())//防止伪唤醒
        {
            td->ThreadSleep();//没有任务那么就去条件变量下等待       
        }
       //如果有任务,那么就将任务拿出来,并执行
       T t=td->Pop();
       td->Unlock();
       
       t();//处理任务
      std::cout<<name<<"run, "<<"reslut: "<<t.Getresult()<<std::endl;
      }

    }
     //当我们去调用这个线程池的时候,线程池就应该给我们创建若干个线程在线程池里。而当有人往线程池里发送任务时,线程
     //池里的线程会立刻被唤醒,去竞争任务
     
     void Start()//线程池里的线程刚被创建出来,就会去线程池里的队列里竞争任务,如果没有任务,那么它就会去休眠
     {
      
      int num=_thread.size();
      for(int i=0;i<num;i++)
      {
         
        _thread[i].name="thread- "+std::to_string(i+1);

        pthread_create(&(_thread[i].tid),nullptr,Handler,this);
      }   
     }

     ~ThreadPool()
     {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
     }

private:
 

  std::vector<ThreadInfo> _thread;//根据这个来找到要分配任务的线程,线程池里存储的线程
  std::queue<T> _task;//线程池里存放的任务

  pthread_mutex_t _mutex;
  pthread_cond_t _cond;
   
};
#include <iostream>
#include "ThreadPool.hpp"
#include "TASK.hpp"
#include <ctime>
#include <unistd.h>
int main()
{

    ThreadPool<TASK> *tp = new ThreadPool<TASK>();
    tp->Start();
    int len = opera.size();
    srand(time(nullptr));
    while (true)
    {
        // 1.获取数据
        int x = rand() % 10 + 1;
        usleep(10);
        int y = rand() % 10;
        char op = opera[rand() % len];
        TASK t(x, y, op);
        // 2.生产数据
        tp->Push(t);

        std::cout<<"main thread make task"<<t.GetTASK()<<std::endl;

        sleep(1);
    }
}
#pragma once
#include <iostream>
#include <string>

std::string opera="+-*/%";
class TASK
{

public:
    TASK()
    {}
    TASK(int data1, int data2, char op) : _data1(data1), _data2(data2), _oper(op)
    {
    }
    void run()
    {
        switch (_oper)
        {
        case '+':
            _result = _data1 + _data2;
            break;
        case '-':
            _result = _data1 - _data2;
            break;
        case '*':
            _result = _data1 * _data2;
            break;
        case '/':
        {
            if (_data2 == 0)
                _exitcode = 1;
            else
                _result = _data1 / _data2;
        }
        break;
        case '%':
        {
            if (_data2 == 0)
                _exitcode = 2;
            else
                _result = _data1 % _data2;
        }
        break;

        default:
        _exitcode=3;
            break;
        }
    }

    std::string GetTASK()
    {
       std::string r=std::to_string(_data1);
       r+=_oper;
       r+=std::to_string(_data2);
       r+="=?";
       return r;
    }
    std::string Getresult()
    {
        std::string result=std::to_string(_data1);
        result+=_oper;
        result+=std::to_string(_data2);
        result+='=';
        result+=std::to_string(_result);
        result+="[code:";
        result+=std::to_string(_exitcode);
        result+=']';
        return result;
    }
    void operator()()
    {
        run();
    }
private:
    int _data1;
    int _data2;
    char _oper;

    int _result;
    int _exitcode=0;
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot Vue汽车租赁系统

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

QTableWidget 双击单元格修改数据

本章介绍通过双击单元格&#xff0c;进入单元格&#xff0c;进行编辑&#xff0c;并对比是否修改了数据&#xff0c;如果修改了更新到数据库。 其他关于QTableWidget的操作&#xff0c;请查看上一篇文章《QTableWidget 用法-CSDN博客》 修改单元格内容&#xff0c;与原值比较…

jQuery鼠标事件、键盘事件、浏览器事件

鼠标事件 1、.click( ): 点击事件 html文档 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><script src"jQuery.js"></script></head><body><p>haha 1</p&g…

C++面试宝典第23题:乌托邦树

题目 乌托邦树每年经历2个生长周期。每年春天,它的高度都会翻倍。每年夏天,他的高度都会增加1米。对于一颗在春天开始时种下的高为1米的树,问经过指定周期后,树的高度为多少? 输入描述:输入一个数字N(0 <= N <= 1000),表示指定周期。 比如:样例输入为3。 输出描…

web开发学习笔记(13.mybatis基于注解配置)

1.使用mybatis基本步骤 2.引入依赖 <!-- mysql--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency> <!-- mybatis--><dependency><groupId>org…

信息检索与数据挖掘 | (七)概率检索模型

文章目录 &#x1f4da;基本概率论知识&#x1f4da;概率排序原理PRP-probability ranking principle&#x1f4da;二值独立模型BIM-Binary Independence Model&#x1f4da;Okapi BM25模型 出于一些追求完整性的强迫症&#xff0c;开始做考完试了梳理知识点博客的离谱行为&…

【目标检测】YOLOv7算法实现(二):正样本匹配(SimOTA)与损失计算

本系列文章记录本人硕士阶段YOLO系列目标检测算法自学及其代码实现的过程。其中算法具体实现借鉴于ultralytics YOLO源码Github&#xff0c;删减了源码中部分内容&#xff0c;满足个人科研需求。   本篇文章在YOLOv5算法实现的基础上&#xff0c;进一步完成YOLOv7算法的实现。…

分布式深度学习中的数据并行和模型并行

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

HQL,SQL刷题简单查询,基础,尚硅谷

今天刷SQL简单查询&#xff0c;大家有兴趣可以刷一下 目录 相关表数据&#xff1a; 题目及思路解析&#xff1a; 总结归纳&#xff1a; 知识补充&#xff1a; 关于LIKE操作符/运算符 LIKE其他使用场景包括 LIKE模糊匹配情况 相关表数据&#xff1a; 1、student_info表 2、sc…

Centos7 安装redis 详细步骤访问不了github和windows系统下载

windows系统下载 https://hellowindows.cn/ VMware虚拟机安装Windows Server 2016 VL https://blog.csdn.net/qq_37545849/article/details/134828341 VMware全屏时不显示上方命令栏的边缘 此时如果要返回&#xff0c;可以把鼠标移动至屏幕上方边缘短暂停留以呼出命令栏。或使…

龙芯3A6000_通过xrdp远程访问统信UOS

原文链接&#xff1a;龙芯3A6000|通过xrdp远程访问统信UOS hello&#xff0c;大家好&#xff01;今天我带给大家的是一篇实用性极强的技术文章——通过xrdp远程访问装载在龙芯3A6000上的统信UOS操作系统。这意味着&#xff0c;无论您使用的是Windows、MACOS还是Linux操作系统&a…

测试 yolov8 分割模型 边缘检测

发现 cfg/default.yaml 参数 mask_ratio 等于4 直接训练如下边缘分割标签,推理时mask 稀疏&#xff0c;训练时分数偏低,mask_ratio 改为1训练时打印的mask 的 P指标一直为0,将imgsz原图size 训练分数也不高 标注用的是labelme多边形 阅读源码发现可能是因为mask缩放导致 且出现…

分类预测 | Matlab实现LSTM-Attention-Adaboost基于长短期记忆网络融合注意力机制的Adaboost数据分类预测/故障识别

分类预测 | Matlab实现LSTM-Attention-Adaboost基于长短期记忆网络融合注意力机制的Adaboost数据分类预测/故障识别 目录 分类预测 | Matlab实现LSTM-Attention-Adaboost基于长短期记忆网络融合注意力机制的Adaboost数据分类预测/故障识别分类效果基本描述程序设计参考资料 分类…

【江科大】STM32:(超级详细)定时器输出比较

文章目录 输出比较单元特点 高级定时器&#xff1a;均有4个通道 PWM简介PWM&#xff08;Pulse Width Modulation&#xff09;脉冲宽度调制输出比较通道PWM基本结构基本定时器 参数计算捕获/比较通道的输出部分详细介绍如下&#xff1a; 舵机介绍硬件电路 直流电机介绍&#xff…

Python教程48:海龟画图turtle画太极八卦阵

---------------turtle源码集合--------------- Python教程91&#xff1a;关于海龟画图&#xff0c;Turtle模块需要学习的知识点 Python源码45&#xff1a;海龟画图turtle画雪容融 Python源码44&#xff1a;海龟画图turtle&#xff0c;画2022卡塔尔世界杯吉祥物 Python教程…

R语言简介

1.R语言 R语言是一种数学编程语言&#xff0c;主要用于统计分析、绘图和数据挖掘。 2.R语言特点 免费、开源&#xff0c;兼容性好&#xff08;Windows、MacOS或Linux)。具有多种数据类型&#xff0c;如向量、矩阵、因子、数据集等常用数据结构。多用于交互式数据分析&#x…

C语言第六弹---分支语句(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 分支语句 1、 逻辑操作符&#xff1a;&& , || , &#xff01;4.1、 逻辑取反运算符 &#xff01;4.2、 与运算符4.3、 或运算符4.4、 练习&#xff1a;闰…

怎么给wordpress网站底部页脚添加备案号和链接?

以前“WordPress后台 >> 常规”最底部是有一个ICP备案号的&#xff0c;我们只需要填写备案号并保存更改即可让WordPress自带主题底部显示ICP备案号&#xff0c;但是现在新版本的WordPress已经没有了这个ICP备案号选项&#xff0c;而且也无法直接添加公安联网备案号&#…

内网环境横向移动——利用DCOM(1)

这里先提两个概念&#xff0c;COM和DCOM COM即组件对象模型(Component Object Model&#xff0c;COM) &#xff0c;是基于 Windows 平台的一套组件对象接口标准&#xff0c;由一组构造规范和组件对象库组成。COM是许多微软产品和技术&#xff0c;如Windows媒体播放器和Windows …

耳鸣是怎么回事呢?

什么是耳鸣&#xff1f; 耳鸣是指在没有任何客观声响的情况下&#xff0c;个人主观上却感觉听到声音&#xff0c;有些人甚至觉得声音来自头部。耳鸣的感觉因人而异&#xff0c;声音多种多样。比如明明没有开任何电器&#xff0c;但却可以感觉到电流声&#xff0c;明明旁边没有…