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

目录

线程池

介绍

图解

过程

本质

模拟实现

思路

注意点 

解决方法

代码

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;
}

示例

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

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

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

相关文章

C++ “雪花算法“原理

C雪花算法并不是传统的数据结构与算法而是一种崭新的分布式算法 属于深层次C 本篇文章就来描述一下雪花算法 什么是雪花算法: 雪花算法&#xff08;Snowflake&#xff09;是Twitter开源的一种分布式唯一ID生成算法。它可以在不依赖于数据库等其他存储设施的情况下&#xff0c…

指针的经典笔试题

经典的指针试题&#xff0c;让你彻底理解指针 前言 之前对于指针做了一个详解&#xff0c;现在来看一些关于指针的经典面试题。 再次说一下数组名 数组名通常表示的都是首元素的地址&#xff0c;但是有两个意外&#xff0c;1.sizeof&#xff08;数组名&#xff09;这里数组名…

ES入门知识点总结

目录 倒排索引 倒排索引 Elasticsearch的倒排索引是一种数据结构&#xff0c;用于加快基于文本的搜索操作。它的主要优势在于能够快速找到包含特定单词的文档。 倒排索引的构建过程如下&#xff1a; 文档分词&#xff1a;将文档内容分割成单独的词&#xff08;或者更小的词元…

Java的异常体系

一、体系简介 java中的Exception类的子类不仅仅只是像上图所示只包含IOException和RuntimeException这两大类&#xff0c;事实上Exception的子类很多很多&#xff0c;主要可概括为&#xff1a;运行时异常与非运行时异常。 在上述体系中&#xff0c;Error表示严重的系统错误&am…

【前端高频面试题--Vue路由篇】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;前端高频面试题 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac;前端高频面试题--Vue路由篇 对Vue-Router的理解Vue路由懒加载的实现路由的hash和history模式如何获…

车载诊断协议DoIP系列 —— 车辆以太网节点需求汇总

车载诊断协议DoIP系列 —— 车辆以太网节点需求汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,…

【剪辑必备】今天我教你如何手动去下载苹果官网4K预告片 完全免费

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…

【开源】新生报到网站 JAVA+Vue.js+SpringBoot+MySQL

本文项目编号&#xff1a; T 002 。 \color{red}{本文项目编号&#xff1a;T002。} 本文项目编号&#xff1a;T002。 目录 1 功能模块1.1 在线交流模块1.2宿舍分配模块1.3 校园概况模块1.4 专业管理模块 2 系统展示3 核心代码3.1 图表展示3.2 查询评论3.3 新增报道 4 免责声明 …

儿时游戏“红色警戒”之“AI警戒”

一、红色警戒里“警戒”命令背后的算法原理是什么 在《红色警戒》系列即时战略游戏中&#xff0c;“警戒”命令背后的算法原理相对简单但又实用&#xff0c;其核心目标是让单位能够自动检测并反击一定范围内的敌方单位。虽然具体的实现细节未公开&#xff0c;但可以推测其基本…

【C++】类和对象(五)友元、内部类、匿名对象

前言&#xff1a;前面我们说到类和对象是一个十分漫长的荆棘地&#xff0c;今天我们将走到终点&#xff0c;也就是说我们对于&#xff23;算是正式的入门了。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质量&#xff23;学习 &…

C#根据权重抽取随机数

&#xff08;游戏中一个很常见的简单功能&#xff0c;比如抽卡抽奖抽道具&#xff0c;或者一个怪物有多种攻击动作&#xff0c;按不同的权重随机出个攻击动作等等……&#xff09; 假如有三种物品 A、B、C&#xff0c;对应的权重分别是A&#xff08;50&#xff09;&#xff0c…

寒假项目-酒店综合管理系统

目前所学的东西依然很有限&#xff0c;难以完成项目&#xff0c;目前只编写了部分代码加以参考。 test.c #ifndef __TEST_H__ #define SER_PORT 8888 //服务器端口号 #define SER_IP "192.168.&#xff1f;.&#xff1f;" //服务器IP地址 #…

C#上位机与三菱PLC的通信03--MC协议之A-1E报文解析

1、MC协议帧 MC协议可以在串口通信&#xff0c;也可以在以太网通信&#xff0c;有A-1E和Qna-3E两种模式&#xff0c;这两种都是三菱PLC通信协议中比较常用的两种&#xff0c;一般我们使用比较多的是以太网通信&#xff0c;对于FX5U系列/Q系列/Qna系列/L系列的PLC&#xff0c;…

糟糕,接口被刷了,怎么办?

前言 在面试时&#xff0c;经常会被问一个问题&#xff1a;如何防止别人恶意刷接口&#xff1f; 这是一个非常有意思的问题&#xff0c;防范措施挺多的。今天这篇文章专门跟大家一起聊聊&#xff0c;希望对你会有所帮助。 1 防火墙 防火墙是网络安全中最基本的安全设备之一&…

Python eval函数

在Python编程中&#xff0c;eval()函数是一个强大且灵活的内置函数&#xff0c;用于动态执行字符串表达式或代码。尽管eval()函数具有强大的功能&#xff0c;但它也带来了一些潜在的安全风险&#xff0c;因此在使用时需要谨慎。本文将深入探讨eval()函数的用法、语法、示例代码…

AI:129-基于深度学习的极端天气事件预警

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

Kibana:如何嵌入 Kibana 仪表板

作者&#xff1a;Carly Richmond 像我这样的前端工程师经常提出的要求是将 Kibana 等来源的现有仪表板嵌入到 JavaScript Web 应用程序中。 这是我必须多次执行的任务&#xff0c;因为我们希望快速部署用户生成的视图或允许用户控制给定的视图。 从我们从精彩的开发者社区收到的…

安装 Windows Server 2019

1.镜像安装 镜像安装:Windows Server 2019 2.安装过程(直接以图的形式呈现) 先选择""我没有产品密钥"",选择桌面体验 选择自定义 设置密码后继续 安装成功

算法——组合数学——二项式定理

杨辉三角是二项式系数的典型应用当 n 较大&#xff0c;且需要取模时&#xff0c;二项式系数有两种计算方法&#xff1a; 一&#xff1a;递推公式&#xff0c;二&#xff1a;逆 方法一&#xff1a;用递推公式计算二项式系数 public class BinomialCoefficient {public static i…

【数据结构】16 二叉树的定义,性质,存储结构(以及先序、后序、中序遍历)

二叉树 一个二叉树是一个有穷的结点集合。 它是由根节点和称为其左子树和右子树的两个不相交的二叉树组成的。 二叉树可具有以下5种形态。 性质 一个二叉树第i层的最大结点数为 2 i − 1 2^{i-1} 2i−1, i ≥ 1 i \geq 1 i≥1 每层最大结点可以对应完美二叉树&#xff08;…