线程池(图解,本质,模拟实现代码)

目录

线程池

介绍

图解

过程

本质

模拟实现

思路

注意点 

解决方法

代码

pthread_pool.hpp

 task.hpp

main.cpp

示例


线程池

介绍

线程池是一种并发编程的设计模式,用于管理和重复使用线程,以提高多线程应用程序的性能和效率

线程池主要用于控制并发执行的线程数量,避免在需要执行任务时频繁创建和销毁线程,从而减少系统资源的开销

简单来说,就是用空间换时间

  • 比如说,malloc封装的系统调用是有代价的
  • 所以每次开辟时都会额外开辟一些空间,这样可以有效减少调用的次数,减少成本

图解

过程

主线程只负责push任务,由线程池完成其他工作

  • 创建线程
  • 分配任务 (当队列中有任务时,就让某个线程去处理;没有时,线程进行等待)

本质

  • 上面的过程听着是不是很熟悉,将任务push进队列,线程又将任务pop出来去处理
  • 其实就是我们已经学习了两种的生产消费者模型
  • 只是要将多线程封装进类中而已

模拟实现

思路

  • 还是熟悉的模型,类中要实现核心的push和pop操作
  • 以及处理好线程的创建和销毁

注意点 

我们的所有操作都是放在类中的 -- 这是一个大前提

  • 其中,pthread_create创造出的线程,其执行任务的函数要求参数有且仅有void*这一个类型
  • 但是该函数是被我们放在类内的,会强制添加一个this指针参数
  • 所以,参数个数就不满足要求了
解决方法
  • 放在类外(全局函数)
  • 静态成员函数

代码

pthread_pool.hpp
#include <pthread.h>
#include <vector>
#include <queue>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <semaphore.h>
#include <iostream>struct thread
{pthread_t tid_;std::string name_;
};template <class T>
class thread_pool
{
private:void lock(){pthread_mutex_lock(&mutex_);}void unlock(){pthread_mutex_unlock(&mutex_);}void wait(){pthread_cond_wait(&cond_, &mutex_);}void signal(){pthread_cond_signal(&cond_);}T pop(){T t = task_.front();task_.pop();return t;}bool is_empty(){return task_.size() == 0;}std::string get_name(pthread_t tid){for (auto &it : threads_){if (it.tid_ == tid){return it.name_;}}return "none";}static void *entry(void *args) // 类成员会有this参数,但入口函数不允许有多余参数{thread_pool<T> *tp = static_cast<thread_pool<T> *>(args); // this指针,用于拿到成员变量/函数while (true){tp->lock();while (tp->is_empty()){tp->wait();}T t = tp->pop();tp->unlock();t();std::cout << "im " << tp->get_name(pthread_self()) << ",task is " << t.get_task() << " ,result is " << t.get_result() << " ,code is " << t.get_code() << std::endl;usleep(20);}return nullptr;}public:thread_pool(int num = 5): num_(num), threads_(num){pthread_cond_init(&cond_, nullptr);pthread_mutex_init(&mutex_, nullptr);}~thread_pool(){pthread_cond_destroy(&cond_);pthread_mutex_destroy(&mutex_);}void init(){for (size_t i = 0; i < num_; ++i){pthread_create(&(threads_[i].tid_), nullptr, entry, this);std::string name = "thread-" + std::to_string(i + 1);threads_[i].name_ = name;}}void join(){for (size_t i = 0; i < num_; ++i){pthread_join(threads_[i].tid_, nullptr);}}void push(const T data){lock();task_.push(data);signal(); // 放在锁内,确保只有当前线程执行唤醒操作,不然可能会有多次操作unlock();}private:std::vector<thread> threads_;std::queue<T> task_;int num_;pthread_cond_t cond_;pthread_mutex_t mutex_;
};
 task.hpp
#pragma once
// 生成二元整数运算任务(加减乘除),有错误码提示
// 1为/0操作,2为%0操作,3为非法错误#include <iostream>
#include <string>using namespace std;string symbol = "+-*/%";
int sym_size = symbol.size();class Task
{
public:Task() {} // 方便只是为了接收传参而定义一个对象Task(int x, int y, char c): x_(x), y_(y), code_(0), op_(c), res_(0){}int get_result(){return res_;}int get_code(){return code_;}string get_task(){string task = to_string(x_) + op_ + to_string(y_) + " = ?";return task;}void operator()(){switch (op_){case '+':res_ = x_ + y_;break;case '-':res_ = x_ - y_;break;case '*':res_ = x_ * y_;break;case '/':if (y_ == 0){code_ = 1;break;}res_ = x_ / y_;break;case '%':if (y_ == 0){code_ = 2;break;}res_ = x_ % y_;break;default:code_ = 3;break;}}
private:int x_;int y_;int res_;int code_;char op_;
};
main.cpp
#include "thread_pool.hpp"
#include "Task.hpp"
#include <random>
#include <time.h>
#include <unistd.h>int main()
{thread_pool<Task> *tp = new thread_pool<Task>;tp->init();while (true){int x = rand() % 10 + 1;usleep(30);int y = rand() % 5;usleep(30);char op = symbol[rand() % (sym_size - 1)];Task task(x, y, op);cout << "task is " << task.get_task() << endl;tp->push(task);sleep(1);}tp->join();return 0;
}

示例

按照预想的那样,主线程放入一个任务,就会有一个线程去处理:

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

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

相关文章

【C++】C++11上

C11上 1.C11简介2.统一的列表初始化2.1 {} 初始化2.2 initializer_list 3.变量类型推导3.1auto3.2decltype3.3nullptr 4.范围for循环5.final与override6.智能指针7. STL中一些变化8.右值引用和移动语义8.1左值引用和右值引用8.2左值引用与右值引用比较8.3右值引用使用场景和意义…

【网站项目】023实验室耗材管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Spring中的事务实现、失效场景即AOP的相关概念理解

spring实现事务&#xff08;声明式事务&#xff09;的本质就是aop完成的,它会对方法的前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据执行情况提交或回滚事务。aop就是面向切面编程&#xff0c;在spring中将那些与业务无关&#xff0c;但却对多个对象产生影响的…

STM32,嵌入式系统中的I2C协议

I2C协议——读写EEPROM 关注我&#xff0c;共同交流&#xff0c;一起成长 前言一、协议简介二、I2C特性及架构三、通信过程 前言 这是一种主要用于集成电路和集成电路&#xff08;IC&#xff09;通信&#xff0c;计算机中复杂的问题大多数就是用分层来进行解决&#xff0c;这个…

关于内存相关的梳理

1 关键字 总结 &#xff08;lowmemory&#xff0c;anr in&#xff09; 2 知识储备 虚拟机原理 垃圾回收算法 又包含标记 和清除两种算法 标记&#xff1a;程序计数器-已过时&#xff0c;可达性分析 具体可见 http://help.eclipse.org/luna/index.jsp?topic%2Forg.ec…

什么是 Docker 容器?以及操作 Docker 容器相关的命令汇总

镜像仓库常用指令&#xff1a;Docker 镜像仓库是什么&#xff1f;有哪些镜像仓库命令&#xff1f; 镜像常用指令&#xff1a;操作 Docker 镜像的常用命令 1. 什么是容器&#xff1f; 容器是镜像的运行实体。容器是基于镜像创建的可运行实例&#xff0c;并且单独存在&#xff0…

打字侠网站,提供免费的双拼打字练习

在当今信息时代&#xff0c;电脑已成为人们生活、学习和工作不可或缺的一部分。随着电脑的普及和广泛应用&#xff0c;打字成了一项必备的技能&#xff0c;尤其是对于从事编程和写作等工作的人来说甚至更为重要。而要想提高打字速度和准确度&#xff0c;良好的打字练习是必不可…

BMS再进阶(新能源汽车电池管理系统)

引言 一文入门BMS&#xff08;电池管理系统&#xff09;_bms电池管理-CSDN博客 BMS进阶&#xff08;Type-C、PD快充、充电IC、SOC算法、电池管理IC&#xff09;_充电ic asi aso功能-CSDN博客 本文是上面两篇博客的续篇&#xff0c;之前都是讲解一些BMS基本原理&#xff0c;…

算法之贪心

1.部分背包问题 代码1&#xff1a; 代码2&#xff1a; 但如果金币不能分割&#xff0c;那贪心就不是最优解&#xff0c;正确的做法是搜索或动态规划。 2.排队接水 3.在规定时间内参加最多的比赛 4.合并果子 使用memset初始化int数组时&#xff0c;第二个参数如果是0&#xff0…

《合成孔径雷达成像算法与实现》Figure6.18

% rho_r c/(2*Fr)而不是rho_r c/(2*Bw) % Hsrcf exp函数里忘记乘pi了 clc clear close all参数设置 距离向参数设置 R_eta_c 20e3; % 景中心斜距 Tr 2.5e-6; % 发射脉冲时宽 Kr 20e12; % 距离向调频率 alpha_os_r 1.2; …

AJAX——接口文档

1 接口文档 接口文档&#xff1a;描述接口的文章 接口&#xff1a;使用AJAX和服务器通讯时&#xff0c;使用的URL&#xff0c;请求方法&#xff0c;以及参数 传送门&#xff1a;AJAX阶段接口文档 <!DOCTYPE html> <html lang"en"><head><meta c…