【探索Linux】—— 强大的命令行工具 P.23(线程池 —— 简单模拟)

news2024/9/22 13:23:57

在这里插入图片描述

阅读导航

  • 引言
  • 一、线程池简单介绍
  • 二、Linux下线程池代码
    • ⭕Makefile文件
    • ⭕ . h 头文件
      • ✅Task.hpp
      • ✅thread.hpp
      • ✅threadPool.hpp
    • ⭕ . cpp 文件
      • ✅testMain.cpp
  • 三、线程池的优点
  • 温馨提示

引言

在Linux下,线程池是一种常见的并发编程模型,它能够有效地管理多个线程,提高系统的性能和资源利用率。通过线程池,可以实现多生产者多消费者模型,有效地处理并发任务,提升系统的响应速度和吞吐量。在本文中,我们将深入探讨如何在Linux环境下创建线程池,以及线程池的实现原理和使用技巧。通过深入理解线程池的概念和应用,我们可以更好地应对复杂的并发编程场景,从而提升系统的稳定性和性能表现。让我们一起探索Linux下线程池的奥秘,为并发编程的世界增添新的色彩!

一、线程池简单介绍

线程池是一种并发编程技术,用于管理和复用多个线程,以提高系统的性能和资源利用率。在线程池中,一定数量的线程被预先创建并保存在池中,当需要执行任务时,从线程池中选择一个空闲的线程来处理任务,任务执行完毕后,线程将返回到线程池中等待下一个任务。

通过使用线程池,可以避免频繁地创建和销毁线程,减少了线程创建和销毁的开销,同时也控制了并发线程的数量,避免系统资源被过度占用。线程池还可以根据系统负载情况动态调整线程数量,以更好地适应不同的工作负载。

二、Linux下线程池代码

⭕Makefile文件

thread_pool:testMain.cpp
	g++ -o $@ $^ -std=c++11 -lpthread #-DDEBUG_SHOW
clean:
	rm -f thread_pool

这个 Makefile 包含了两个规则:一个用于编译名为"testMain.cpp"的程序并生成名为"thread_pool"的可执行文件,另一个用于清理生成的可执行文件。你可以使用"make"命令编译程序,使用"make clean"命令清理生成的可执行文件。

⭕ . h 头文件

✅Task.hpp

#pragma once

#include <iostream>
#include <string>
#include <functional>

// 定义函数类型 func_t,用于表示可以接受两个整型参数并返回一个整型结果的函数
typedef std::function<int(int, int)> func_t;

// Task 类,表示一个任务
class Task
{
public:
    // 默认构造函数
    Task() {}

    // 带参数的构造函数,初始化任务的成员变量
    Task(int x, int y, func_t func) : x_(x), y_(y), func_(func)
    {}

    // 重载 () 运算符,实现任务的执行
    void operator()(const std::string &name)
    {
        // 在控制台输出任务执行的结果
        std::cout << "线程 " << name << " 处理完成, 结果是: " << x_ << "+" << y_ << "=" << func_(x_, y_) << std::endl;
    }

public:
    int x_; // 任务的参数 x
    int y_; // 任务的参数 y
    func_t func_; // 保存任务所需执行的函数
};


✅thread.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstdio>

// 定义函数指针类型 fun_t,表示可以接受一个 void* 类型参数并返回一个 void* 类型结果的函数指针
typedef void *(*fun_t)(void *);

// 线程数据结构,用于保存线程的参数和名称
class ThreadData
{
public:
    void *args_; // 线程参数
    std::string name_; // 线程名称
};

// 线程类,用于创建和管理线程
class Thread
{
public:
    // 构造函数,初始化线程对象
    Thread(int num, fun_t callback, void *args) : func_(callback)
    {
        // 设置线程名称为 "Thread-num"
        char nameBuffer[64];
        snprintf(nameBuffer, sizeof nameBuffer, "Thread-%d", num);
        name_ = nameBuffer;

        // 设置线程数据的参数和名称
        tdata_.args_ = args;
        tdata_.name_ = name_;
    }

    // 启动线程
    void start()
    {
        pthread_create(&tid_, nullptr, func_, (void*)&tdata_);
    }

    // 等待线程结束
    void join()
    {
        pthread_join(tid_, nullptr);
    }

    // 获取线程名称
    std::string name()
    {
        return name_;
    }

    // 析构函数
    ~Thread()
    {
    }

private:
    std::string name_; // 线程名称
    fun_t func_;       // 线程执行的函数指针
    ThreadData tdata_; // 线程数据
    pthread_t tid_;    // 线程 ID
};


✅threadPool.hpp

#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>
#include "thread.hpp"

// 互斥锁类,封装了互斥锁的操作
class Mutex
{
public:
    Mutex(pthread_mutex_t *mtx) : pmtx_(mtx)
    {
    }
    void lock()
    {
        pthread_mutex_lock(pmtx_);
    }
    void unlock()
    {
        pthread_mutex_unlock(pmtx_);
    }
    ~Mutex()
    {
    }

private:
    pthread_mutex_t *pmtx_;
};

// RAII风格的加锁方式,构造时加锁,析构时解锁
class lockGuard
{
public:
    lockGuard(pthread_mutex_t *mtx) : mtx_(mtx)
    {
        mtx_.lock();
    }
    ~lockGuard()
    {
        mtx_.unlock();
    }

private:
    Mutex mtx_;
};

// 默认线程数
const int g_thread_num = 3;

// 线程池类模板
template <class T>
class ThreadPool
{
public:
    // 获取互斥锁指针
    pthread_mutex_t *getMutex()
    {
        return &lock;
    }
    // 判断任务队列是否为空
    bool isEmpty()
    {
        return task_queue_.empty();
    }
    // 等待条件变量
    void waitCond()
    {
        pthread_cond_wait(&cond, &lock);
    }
    // 获取待执行任务
    T getTask()
    {
        T t = task_queue_.front();
        task_queue_.pop();
        return t;
    }

private:
    // 构造函数,初始化线程池
    ThreadPool(int thread_num = g_thread_num) : num_(thread_num)
    {
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&cond, nullptr);
        for (int i = 1; i <= num_; i++)
        {
            threads_.push_back(new Thread(i, routine, this));
        }
    }
    ThreadPool(const ThreadPool<T> &other) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T> &other) = delete;

public:
    // 获取线程池单例
    static ThreadPool<T> *getThreadPool(int num = g_thread_num)
    {
        if (nullptr == thread_ptr)
        {
            lockGuard lockguard(&mutex);
            if (nullptr == thread_ptr)
            {
                thread_ptr = new ThreadPool<T>(num);
            }
        }
        return thread_ptr;
    }
    // 启动线程池中的线程
    void run()
    {
        for (auto &iter : threads_)
        {
            iter->start();
            std::cout << iter->name() << " 启动成功" << std::endl;
        }
    }
    // 线程执行的函数
    static void *routine(void *args)
    {
        ThreadData *td = (ThreadData *)args;
        ThreadPool<T> *tp = (ThreadPool<T> *)td->args_;
        while (true)
        {
            T task;
            {
                lockGuard lockguard(tp->getMutex());
                while (tp->isEmpty())
                    tp->waitCond();
                task = tp->getTask();
            }
            task(td->name_);
        }
    }
    // 向任务队列中添加任务
    void pushTask(const T &task)
    {
        lockGuard lockguard(&lock);
        task_queue_.push(task);
        pthread_cond_signal(&cond);
    }
    // 析构函数,销毁线程池
    ~ThreadPool()
    {
        for (auto &iter : threads_)
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }

private:
    std::vector<Thread *> threads_; // 线程对象数组
    int num_; // 线程数量
    std::queue<T> task_queue_; // 任务队列

    static ThreadPool<T> *thread_ptr; // 线程池单例指针
    static pthread_mutex_t mutex; // 线程池单例的互斥锁

    pthread_mutex_t lock; // 线程池内部使用的互斥锁
    pthread_cond_t cond; // 线程池内部使用的条件变量
};

// 静态成员初始化
template <typename T>
ThreadPool<T> *ThreadPool<T>::thread_ptr = nullptr;

template <typename T>
pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;

⭕ . cpp 文件

✅testMain.cpp

#include "threadPool.hpp" // 包含线程池头文件
#include "Task.hpp" // 包含任务类的头文件
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <unistd.h>

int main()
{
    srand((unsigned long)time(nullptr) ^ getpid()); // 初始化随机数种子

    // 获取线程池单例并运行线程
    ThreadPool<Task>::getThreadPool()->run();

    while(true)
    {
        // 生产任务的过程,制作任务的时候,要花时间
        int x = rand() % 100 + 1; // 随机生成一个范围在1到100之间的整数x
        usleep(7721); // 模拟制作任务需要的时间
        int y = rand() % 30 + 1; // 随机生成一个范围在1到30之间的整数y
        Task t(x, y, [](int x, int y) -> int { // 创建任务对象t,执行加法操作
            return x + y;
        });

        std::cout << "制作任务完成: " << x << "+" << y << "=?" << std::endl; // 输出任务信息

        // 推送任务到线程池中
        ThreadPool<Task>::getThreadPool()->pushTask(t);

        sleep(1); // 暂停一秒钟
    }
    return 0;
}

这段代码主要实现了一个简单的任务生产者,不断地生成任务并将任务推送到线程池中执行。

三、线程池的优点

线程池能够有效地管理线程,提高系统的性能和响应速度,同时简化了线程管理的复杂性,是多线程编程中常用的一种技术。下面是它的优点

  1. 提高性能:线程池可以减少线程创建和销毁的开销,通过重用线程,避免了频繁地创建和销毁线程所带来的性能损耗。

  2. 控制并发度:线程池可以限制同时执行的线程数量,从而控制系统的并发度,防止因为过多线程导致系统资源被耗尽。

  3. 提高响应速度:由于线程池中的线程已经创建好并处于就绪状态,当任务到达时可以立即执行,从而减少了任务等待执行的时间,提高了系统的响应速度。

  4. 简化线程管理:线程池封装了线程的创建、销毁、调度等操作,简化了线程管理的复杂性,提高了代码的可维护性。

  5. 控制资源占用:线程池可以限制系统中同时存在的线程数量,从而控制系统对资源(如内存、CPU)的占用,防止资源被耗尽导致系统崩溃。

总而言之,线程池提供了一种高效、可控的线程管理机制,适用于处理大量并发任务的场景,是提高系统性能和响应速度的重要工具之一。

温馨提示

感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

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

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

相关文章

C++ Primer Plus 笔记(持续更新)

编译器的正解 数据&#xff0b;算法程序 赋值从右向左进行 cin&#xff0c;cout的本质也是对象 类和对象的解释

扫盲贴:Svg动画和Canvas动画有什么区别

hello&#xff0c;我是贝格前端工场&#xff0c;网页中动画的实现有N种方式&#xff0c;比如css动画&#xff0c;js动画&#xff0c;svg动画&#xff0c;canvas动画等等&#xff0c;每一种动画都有对应的场景&#xff0c;本问重点介绍一下svg和canvas动画的异同点&#xff0c;欢…

基于SpringBoot的家教管理系统

基于SpringBootVue的家教管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台主页 家教 个人中心 管理员界面 摘要 本文介绍了基于SpringBoot框架开发的家…

我花了5天时间,开发了一个在线学习的小网站

大三寒假赋闲在家&#xff0c;闲来无事&#xff0c;用了5天时间做了一个在线学习的小网站&#xff0c;一鼓作气部署上线&#xff0c;制作的过程比较坎坷。内心经历过奔溃&#xff0c;也经历过狂喜。 按照惯例先放出网址&#xff0c;欢迎大家来访问学习&#xff1a;www.pbjlove…

KNN算法使用模拟PLINKO规则,计算出概率最接近的投掷位子

模拟器 以PLINKO为例,利用程序,模拟出来,如: 投放位置与落入点关系 思考: 在300处丢下圆盘, 预计会落入哪个篮筐? 流程梳理 1. 收集数据集记录多次的实现数

Minimize Inversions

先来看看官方题解的做法&#xff0c;他一反常态的没有在逆序对题目里面考虑每个位置的贡献&#xff0c;而是直接回到定义考虑每对数是否是逆序对 我们考虑原数列中任意的一组数\((a_i,a_j)\)和\((b_i,b_j)\)。如果最开始两个都不是逆序对&#xff0c;那么交换之后两个都是逆序对…

Uniapp小程序开发-底部tabbar的开发思路

文章目录 前言一、uniapp 实现 tabbar二、图标使用网络图片后端返回tabbar信息uniapp方式中的setTabBarItem 总结 前言 记录uniapp 开发小程序的底部tabbar &#xff0c;这里讨论的不是自定义tabbar的情况。而是使用wx.setTabBarItem(Object object) 这个api的情况。关于custo…

【数据结构与算法初学者指南】【冲击蓝桥篇】String与StringBuilder的区别和用法

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

【安装】CentOS 7 使用 OUI 图形界面安装 Oracle Database 19.3

需安装使用 X Server 协议的软件&#xff08;如 Xorg&#xff09;和如桌面图形软件&#xff08;Gnome 或 KDE&#xff09;。 使用 root 用户执行&#xff1a; # curl -o oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm https://yum.oracle.com/repo/OracleLinux/OL7/l…

代码随想录|day 23

Day 23 一、回溯 二、代码 216. 组合总和 III - 力扣&#xff08;LeetCode&#xff09; class Solution { private:vector<int>path;vector<vector<int>>result;void backtracing(int sum,int k,int n,int startindex){//中止条件if(path.size()k){if(sum…

【FreeRTOS】任务创建

参考博客&#xff1a; ESP-IDF FreeRTOS 任务创建分析 - [Genius] - 博客园 (cnblogs.com) 1.什么是任务 1&#xff09;独立的无法返回的函数称为任务 2&#xff09;任务是无线循环 3&#xff09;无返回数据 2.任务的实现过程 1.定义任务栈 裸机程序&#xff1a;统一分配到一…

性能测试的几个指标范围(CPU,内存,IO,网络)

性能测试中&#xff0c;对服务端的指标监控也是很重要的一个环节。通过对各项服务器性能指标的监控分析&#xff0c;可以定位到性能瓶颈。 后端性能指标有 CPU&#xff0c;内存&#xff0c;网络&#xff0c;jvm&#xff0c;I/O 等等 分析思路 整体系统 CPU 利用率 内存利用…

C# If与Switch的区别

在 switch 语句中使用表达式比较时&#xff0c;编译器会生成一个查找表&#xff0c;其中包含所有表达式的值和对应的 case 标签。因此&#xff0c;与使用常量或字面量比较相比&#xff0c;使用表达式比较可能会略微降低性能。 只有当 switch 语句中的所有 case 标签都使用常量或…

AI:136-基于深度学习的图像生成与风格迁移

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

提升装备制造企业竞争力:2023年CRM选型与应用完全解读

在加快产业转型升级的大背景下&#xff0c;高端装备制造业既面临机遇也面临挑战。随着公司规模的不断壮大&#xff0c;再加上装备制造业营销体系及服务体系管理体系的复杂性&#xff0c;一些问题逐渐暴露出来&#xff0c;装备制造业企业需要根据自身业务需求和管理流程选择合适…

C++初阶:容器适配器priority_queue常用接口详解及模拟实现、仿函数介绍

介绍完了stack和queue的介绍以及模拟的相关内容后&#xff1a;C初阶&#xff1a;容器适配器介绍、stack和queue常用接口详解及模拟实现 接下来进行priority_queue的介绍以及模拟&#xff1a; 文章目录 1.priority_queue的介绍和使用1.1priority_queue的初步介绍1.2priority_que…

k8s-helm部署应用 19

Helm部署nfs-client-provisioner&#xff08;存储类&#xff09;&#xff1a; 预先配置好外部的NFS服务器 部署 Helm部署nginx-ingress应用&#xff1a; 添加下载ingress 拉取 解开并修改 部署 测试 回收 helm部署metrics-server&#xff1a; 清除之前的metrics部署 下载…

QT问题 打开Qt Creator发现没有菜单栏

之前不知道按了什么快捷键,当我再次打开Qt Creator时发现菜单栏消失啦 找了许多原因发现:安装有道词典的快捷键Ctrl Alt m 与Qt Creator里的快捷键冲突导致菜单栏被莫名其妙的隐藏 解决方法: 1找到有道词典快捷键 2再次按快捷键 Ctrl Alt m就可以重新显示菜单栏

Linux系统——nginx服务补充

目录 一、Nginx调优补充 1.验证模块——登录 1.1htpasswd 1.1.1htpasswd -c——交互式 1.1.2htpasswd -b——免交互式 2.自定义错误页面 2.1设置错误页面——想让客户端看到的页面 2.2自定义错误码 3.日志位置存放&#xff08;可以自定义存放位置&#xff09; 4.检测…

零基础手把手教你创建微信小程序(二)·创建第一个微信小程序以及了解小程序代码的构成

零基础手把手教你创建微信小程序&#xff08;一&#xff09;微信小程序开发账号的注册以及开发者工具的安装和使用-CSDN博客 目录 ​编辑 1. 创建微信小程序 1.1 基本信息 1.2 在模拟器上查看项目效果 1.3 在真机上预览项目效果 1.4 主界面的5个组成部分 1.4.1 菜单…