Linux:线程池的创建和基本使用

文章目录

  • 预备资源
  • 线程池
    • 基本框架
    • 单例模式
    • 线程池的运行
    • 样例

本篇主要用以实现一个线程池,用来方便后续代码编写等,核心工作是体会线程池的工作原理

预备资源

首先增加一个任务函数,这里直接使用一份写好的任务头文件,该头文件中包含的内容是计算各种数据运算:

// task.hpp
#pragma once
#include <iostream>
#include <string>std::string opers = "+-*/%";enum
{DivZero = 1,ModZero,Unknown
};class Task
{
public:Task(){}Task(int x, int y, char op) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0){}void run(){switch (oper_){case '+':result_ = data1_ + data2_;break;case '-':result_ = data1_ - data2_;break;case '*':result_ = data1_ * data2_;break;case '/':{if (data2_ == 0)exitcode_ = DivZero;elseresult_ = data1_ / data2_;}break;case '%':{if (data2_ == 0)exitcode_ = ModZero;elseresult_ = data1_ % data2_;}break;default:exitcode_ = Unknown;break;}}void operator()(){run();std::cout << GetResult() << std::endl;}std::string GetResult(){std::string r = std::to_string(data1_);r += oper_;r += std::to_string(data2_);r += "=";r += std::to_string(result_);r += "[code: ";r += std::to_string(exitcode_);r += "]";return r;}std::string GetTask(){std::string r = std::to_string(data1_);r += oper_;r += std::to_string(data2_);r += "=?";return r;}~Task(){}private:int data1_;int data2_;char oper_;int result_;int exitcode_;
};

线程池

基本框架

对于线程池来说,基本框架是要有线程池的任务,运行队列,还有必要的锁和环境变量,所以创建一个线程池,其中包含对应的成员变量

template <class T>
class ThreadPool
{
private:// 线程池中所有任务,用顺序表管理起来std::vector<ThreadInfo> threads_;// 当前运行队列中运行的任务,用队列管理起来std::queue<T> tasks_;// 线程所必要的锁和条件变量pthread_mutex_t mutex_;pthread_cond_t cond_;// 全局的线程池索引和锁static ThreadPool<T> *tp_;static pthread_mutex_t lock_;
};

单例模式

一种基本的认识是,线程池不应该有多份资源,正常来说在一份进程中只应该出现一份进程池资源即可,所以这里采用了一个单例模式的方法来实现线程池

// 单例模式,创建或者获取一个线程池,在一份代码中只有一份线程池的实例
static ThreadPool<T> *GetInstance()
{if (nullptr == tp_){pthread_mutex_lock(&lock_);if (nullptr == tp_){std::cout << "log: singleton create done first!" << std::endl;tp_ = new ThreadPool<T>();}pthread_mutex_unlock(&lock_);}return tp_;
}

线程池的运行

针对于线程池的运行,由于有线程的顺序表的存在,所以只需要依次启动线程即可,对于线程执行的函数,只需要在handle处理方法中进行执行即可

    // 对应任务的处理方法static void *HandlerTask(void *args){ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);std::string name = tp->GetThreadName(pthread_self());while (true){tp->Lock();while (tp->IsQueueEmpty()){tp->ThreadSleep();}T t = tp->Pop();tp->Unlock();t();}}// 开始运行线程池的内容,初始化线程的名字,并创建线程执行对应的处理函数方法void Start(){int num = threads_.size();for (int i = 0; i < num; i++){threads_[i].name = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);}}

而核心代码实质上已经结束了,剩下就是对于线程池进行一定程度的封装即可,最终要实现的效果起码是可以对于任务进行推送,并且可以对于任务进行计算和输出

样例

#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>// 对线程的信息进行封装,线程的信息包括线程的id和线程的名字
struct ThreadInfo
{pthread_t tid;std::string name;
};// 设定线程池的最大容量是10个线程
static const int defalutnum = 10;template <class T>
class ThreadPool
{
public:// 对线程的基本操作,加锁解锁唤醒休眠等等void Lock(){pthread_mutex_lock(&mutex_);}void Unlock(){pthread_mutex_unlock(&mutex_);}void Wakeup(){pthread_cond_signal(&cond_);}void ThreadSleep(){pthread_cond_wait(&cond_, &mutex_);}bool IsQueueEmpty(){return tasks_.empty();}// 获取特定线程id对应的线程名std::string GetThreadName(pthread_t tid){for (const auto &ti : threads_){if (ti.tid == tid)return ti.name;}return "None";}public:// 对应任务的处理方法static void *HandlerTask(void *args){ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);std::string name = tp->GetThreadName(pthread_self());while (true){tp->Lock();while (tp->IsQueueEmpty()){tp->ThreadSleep();}T t = tp->Pop();tp->Unlock();t();}}// 开始运行线程池的内容,初始化线程的名字,并创建线程执行对应的处理函数方法void Start(){int num = threads_.size();for (int i = 0; i < num; i++){threads_[i].name = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);}}// 获取当前任务队列中将要处理的任务T Pop(){T t = tasks_.front();tasks_.pop();return t;}// STL并不线程安全,先加锁,放到运行队列中,唤醒线程执行,再释放锁void Push(const T &t){Lock();tasks_.push(t);Wakeup();Unlock();}// 单例模式,创建或者获取一个线程池,在一份代码中只有一份线程池的实例static ThreadPool<T> *GetInstance(){if (nullptr == tp_){pthread_mutex_lock(&lock_);if (nullptr == tp_){std::cout << "log: singleton create done first!" << std::endl;tp_ = new ThreadPool<T>();}pthread_mutex_unlock(&lock_);}return tp_;}private:// 对线程池构造等函数的封装,能够实现单例模式的效果ThreadPool(int num = defalutnum) : threads_(num){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}ThreadPool(const ThreadPool<T> &) = delete;const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
private:// 线程池中所有任务,用顺序表管理起来std::vector<ThreadInfo> threads_;// 当前运行队列中运行的任务,用队列管理起来std::queue<T> tasks_;// 线程所必要的锁和条件变量pthread_mutex_t mutex_;pthread_cond_t cond_;// 全局的线程池索引和锁static ThreadPool<T> *tp_;static pthread_mutex_t lock_;
};template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;

那在实际的使用中,可以和生产消费者模型进行搭配进行使用

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

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

相关文章

如何把在本地存储sessionStorage.setItem()上存的值渲染在输入框中js

首先数据如下: {"id":290,"password":"e10adc3949ba59abbe56e057f20f883e","membercatid":4,"img":"/uploads/20240307/42a5c062d8d260dbfb04fac8cc89ca2a.png","company":"cc科技","na…

bootstrap精选模板tabler下载

官网演示&#xff1a; https://mb.bootcss.com/themes/tabler/index.html 在线预览&#xff1a; https://tabler.io/preview Github 开源地址&#xff1a; https://github.com/tabler/tabler Tabler 项目特点&#xff1a; 现代化设计&#xff1a; Tabler 采用现代化的设计…

代码算法训练营day8 | 344.反转字符串、 541. 反转字符串II、卡码网:54.替换数字、151.翻转字符串里的单词、卡码网:55.右旋转字符串

day8:剩下的两题 151.翻转字符串里的单词卡码网&#xff1a;55.右旋转字符串 151.翻转字符串里的单词 题目链接 状态&#xff1a;不ok 文档&#xff1a;programmercarl.com 思路&#xff1a; 我们将整个字符串都反转过来&#xff0c;那么单词的顺序指定是倒序了&#xff0c;只不…

【2024.3.19练习】统计子矩阵

题目描述 题目分析 这道题一开始没有思路&#xff0c;使用蛮力枚举的方法时间复杂度为&#xff0c;显然超时。 参考题解后学会了化二维问题为一维问题&#xff0c;先使用的复杂度限制子矩阵的高度&#xff0c;再考虑列&#xff0c;这样就将子矩阵的和问题转变为了连续子序列的…

拿捏指针(三)

✨✨欢迎&#x1f44d;&#x1f44d;点赞☕️☕️收藏✍✍评论 个人主页&#xff1a;秋邱博客 所属栏目&#xff1a;C语言 &#xff08;感谢您的光临&#xff0c;您的光临蓬荜生辉&#xff09; 前言 在这之前我们学习了《拿捏指针&#xff08;一&#xff09;》&#xff0c;《拿…

重学SpringBoot3-函数式Web

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-函数式Web 函数式Web编程简介RouterFunctionRequestPredicateServerRequestServerResponse 好处示例结论 随着响应式编程范式的兴起和 Java 函数式编程能…

Spring之@Value注解

前言 Value注解在Spring的依赖注入中占据重要地位,这里对Value注解的作用进行演示以及扩展 作用 注入字符串注入属性注入bean其他 代码准备 创建两个普通的bean Component public class ValueComponent { } Component public class Foo {private String sign;public Foo…

个人工作常用Linux相关总结

bash脚本 更新前端 #!/bin/bash # 定义变量 # 移动zip到相应路径 function move_zip() { if [ -f "$ZIP_FILE" ]; then mv "$ZIP_FILE" "$ZIP_DEST" if [ $? -eq 0 ]; then echo "Zip file moved to $ZIP_DEST" else echo…

day-24 不同路径

思路&#xff1a;动态规划&#xff0c;因为只能向下或向右移动&#xff0c;所以第一行和第一列的路径数皆为1&#xff0c;其余位置的路径数dp[i][j]dp[i-1][j]dp[i][j-1] 最后返回dp[m-1][n-1]即可 code: class Solution {public int uniquePaths(int m, int n) {int dp[][]n…

【通用知识】HttpServletRequest接口方法

一、前端知识概述 说明&#xff1a; 1、Headers和Payload为前端传给后端的请求头和请求参数信息。Preview和Response为后端返回的数据。 2、Payload标签内为前端传给后端的参数。其中&#xff0c;Query String Parameters中为问号传参&#xff0c;对应后端RequestParam方式&…

MNN createRuntime(二)

系列文章目录 MNN createFromBuffer&#xff08;一&#xff09; MNN createRuntime&#xff08;二&#xff09; MNN createSession 之 Schedule&#xff08;三&#xff09; MNN createSession 之创建流水线后端&#xff08;四&#xff09; MNN Session::resize 之流水线编码&am…

飞天使-k8s知识点27-kubernetes温故知新2-deployment

文章目录 RC和RS无状态应用管理 deployment有状态应用statefulSetdaemonSet RC和RS RC不会使用在生产环境 RS 比RC 多了标签选择器 &#xff0c;RS 用deployment管理&#xff0c;用于容器编排无状态应用管理 deployment apiVersion: apps/v1 kind: Deployment metadata:name:…

C# Onnx Yolov9 Detect 物体检测

目录 介绍 效果 项目 模型信息 代码 下载 C# Onnx Yolov9 Detect 物体检测 介绍 yolov9 github地址&#xff1a;https://github.com/WongKinYiu/yolov9 Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information …

jQuery遍历DOM元素

自下而上(获取父节点) 通过jQuery对象可以向上遍历DOM树 parent()返回被选元素的直接父元素。该方法只会向上一级对DOM 树进行遍历。返回要给jQuery对象parents([type]) 返回被选元素的祖先元素可传入字符串过滤类型&#xff0c;返回被选元素中为该类型的祖先元素 parentsUn…

IPD流程学习

集成开发的概念学习&#xff0c; IPD是把产品纳入经营的理念&#xff0c;分为两件事&#xff1a;1是做正确的事&#xff0c;2正确的事情怎么做 1、做正确的事&#xff0c; 市场调研&#xff0c;不是闭门造车需求管理&#xff0c;怎么把客户的需求转变成产品的开发需求&#…

构建Helm chart和chart使用管道与函数简介

目录 一.创建helm chart&#xff08;以nginx为例&#xff09; 1.通过create去创建模板 2.查看模板下的文件 3.用chart模版安装nginx 二.版本更新和回滚问题 1.使用upgrade -f values.yaml或者命令行--set来设置 2.查看历史版本并回滚 三.helm模板内管道和函数 1.defau…

软件工程-第6章 面向对象方法UML

UML是一种图形化语言&#xff0c;简称画图。 6.1 表达客观事物的术语 6.2 表达关系的术语 1.关联 表达关联语义相关术语&#xff1a;关联名、导航、角色、可见性、多重性、限定符、聚合、组合。 2.泛化 3.细化 6.3 组织信息的一种通用机制-包 6.4 模型表达工具 一个用况图包含6…

Springboot+Redis:实现缓存 减少对数据库的压力

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏Redis实战与进阶 本专栏讲解Redis从原理到实践 …

【Vue3】走进Pinia,学习Pinia,使用Pinia

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

杰发科技AC7801——读取Flash数据做CRC校验

查看Keil的编译结果发现总共6160个字节。计算结果如下&#xff0c; 代码如下 #include "ac780x_crc.h" #include "ac780x.h" #include "ac780x_debugout.h" #include "string.h" #include "ac780x_eflash.h"#define TestSi…