【探索Linux】P.23(线程池 —— 简单模拟)

莱莱  金牌会员 | 2024-10-25 08:37:23 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 837|帖子 837|积分 2511



  
引言

在Linux下,线程池是一种常见的并发编程模型,它能够有效地管理多个线程,提高系统的性能和资源利用率。通过线程池,可以实现多生产者多消费者模型,有效地处置惩罚并发任务,提升系统的响应速度和吞吐量。在本文中,我们将深入探讨怎样在Linux情况下创建线程池,以及线程池的实现原理和使用本事。通过深入明白线程池的概念和应用,我们可以更好地应对复杂的并发编程场景,从而提升系统的稳固性和性能表现。让我们一起探索Linux下线程池的奥秘,为并发编程的天下增添新的色彩!
一、线程池简单介绍

线程池是一种并发编程技术,用于管理和复用多个线程,以提高系统的性能和资源利用率。在线程池中,肯定命量的线程被预先创建并保存在池中,当需要执行任务时,从线程池中选择一个空闲的线程来处置惩罚任务,任务执行完毕后,线程将返回到线程池中等待下一个任务。
通过使用线程池,可以克制频仍地创建和销毁线程,减少了线程创建和销毁的开销,同时也控制了并发线程的数量,克制系统资源被过度占用。线程池还可以根据系统负载情况动态调整线程数量,以更好地顺应不同的工作负载。
二、Linux下线程池代码

⭕Makefile文件

  1. thread_pool:testMain.cpp
  2.         g++ -o $@ $^ -std=c++11 -lpthread #-DDEBUG_SHOW
  3. clean:
  4.         rm -f thread_pool
复制代码
这个 Makefile 包含了两个规则:一个用于编译名为"testMain.cpp"的程序并天生名为"thread_pool"的可执行文件,另一个用于清算天生的可执行文件。你可以使用"make"命令编译程序,使用"make clean"命令清算天生的可执行文件。
⭕ . h 头文件

✅Task.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <functional>
  5. // 定义函数类型 func_t,用于表示可以接受两个整型参数并返回一个整型结果的函数
  6. typedef std::function<int(int, int)> func_t;
  7. // Task 类,表示一个任务
  8. class Task
  9. {
  10. public:
  11.     // 默认构造函数
  12.     Task() {}
  13.     // 带参数的构造函数,初始化任务的成员变量
  14.     Task(int x, int y, func_t func) : x_(x), y_(y), func_(func)
  15.     {}
  16.     // 重载 () 运算符,实现任务的执行
  17.     void operator()(const std::string &name)
  18.     {
  19.         // 在控制台输出任务执行的结果
  20.         std::cout << "线程 " << name << " 处理完成, 结果是: " << x_ << "+" << y_ << "=" << func_(x_, y_) << std::endl;
  21.     }
  22. public:
  23.     int x_; // 任务的参数 x
  24.     int y_; // 任务的参数 y
  25.     func_t func_; // 保存任务所需执行的函数
  26. };
复制代码
✅thread.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <cstdio>
  5. // 定义函数指针类型 fun_t,表示可以接受一个 void* 类型参数并返回一个 void* 类型结果的函数指针
  6. typedef void *(*fun_t)(void *);
  7. // 线程数据结构,用于保存线程的参数和名称
  8. class ThreadData
  9. {
  10. public:
  11.     void *args_; // 线程参数
  12.     std::string name_; // 线程名称
  13. };
  14. // 线程类,用于创建和管理线程
  15. class Thread
  16. {
  17. public:
  18.     // 构造函数,初始化线程对象
  19.     Thread(int num, fun_t callback, void *args) : func_(callback)
  20.     {
  21.         // 设置线程名称为 "Thread-num"
  22.         char nameBuffer[64];
  23.         snprintf(nameBuffer, sizeof nameBuffer, "Thread-%d", num);
  24.         name_ = nameBuffer;
  25.         // 设置线程数据的参数和名称
  26.         tdata_.args_ = args;
  27.         tdata_.name_ = name_;
  28.     }
  29.     // 启动线程
  30.     void start()
  31.     {
  32.         pthread_create(&tid_, nullptr, func_, (void*)&tdata_);
  33.     }
  34.     // 等待线程结束
  35.     void join()
  36.     {
  37.         pthread_join(tid_, nullptr);
  38.     }
  39.     // 获取线程名称
  40.     std::string name()
  41.     {
  42.         return name_;
  43.     }
  44.     // 析构函数
  45.     ~Thread()
  46.     {
  47.     }
  48. private:
  49.     std::string name_; // 线程名称
  50.     fun_t func_;       // 线程执行的函数指针
  51.     ThreadData tdata_; // 线程数据
  52.     pthread_t tid_;    // 线程 ID
  53. };
复制代码
✅threadPool.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <vector>
  4. #include <string>
  5. #include <queue>
  6. #include <unistd.h>
  7. #include "thread.hpp"
  8. // 互斥锁类,封装了互斥锁的操作
  9. class Mutex
  10. {
  11. public:
  12.     Mutex(pthread_mutex_t *mtx) : pmtx_(mtx)
  13.     {
  14.     }
  15.     void lock()
  16.     {
  17.         pthread_mutex_lock(pmtx_);
  18.     }
  19.     void unlock()
  20.     {
  21.         pthread_mutex_unlock(pmtx_);
  22.     }
  23.     ~Mutex()
  24.     {
  25.     }
  26. private:
  27.     pthread_mutex_t *pmtx_;
  28. };
  29. // RAII风格的加锁方式,构造时加锁,析构时解锁
  30. class lockGuard
  31. {
  32. public:
  33.     lockGuard(pthread_mutex_t *mtx) : mtx_(mtx)
  34.     {
  35.         mtx_.lock();
  36.     }
  37.     ~lockGuard()
  38.     {
  39.         mtx_.unlock();
  40.     }
  41. private:
  42.     Mutex mtx_;
  43. };
  44. // 默认线程数
  45. const int g_thread_num = 3;
  46. // 线程池类模板
  47. template <class T>
  48. class ThreadPool
  49. {
  50. public:
  51.     // 获取互斥锁指针
  52.     pthread_mutex_t *getMutex()
  53.     {
  54.         return &lock;
  55.     }
  56.     // 判断任务队列是否为空
  57.     bool isEmpty()
  58.     {
  59.         return task_queue_.empty();
  60.     }
  61.     // 等待条件变量
  62.     void waitCond()
  63.     {
  64.         pthread_cond_wait(&cond, &lock);
  65.     }
  66.     // 获取待执行任务
  67.     T getTask()
  68.     {
  69.         T t = task_queue_.front();
  70.         task_queue_.pop();
  71.         return t;
  72.     }
  73. private:
  74.     // 构造函数,初始化线程池
  75.     ThreadPool(int thread_num = g_thread_num) : num_(thread_num)
  76.     {
  77.         pthread_mutex_init(&lock, nullptr);
  78.         pthread_cond_init(&cond, nullptr);
  79.         for (int i = 1; i <= num_; i++)
  80.         {
  81.             threads_.push_back(new Thread(i, routine, this));
  82.         }
  83.     }
  84.     ThreadPool(const ThreadPool<T> &other) = delete;
  85.     const ThreadPool<T> &operator=(const ThreadPool<T> &other) = delete;
  86. public:
  87.     // 获取线程池单例
  88.     static ThreadPool<T> *getThreadPool(int num = g_thread_num)
  89.     {
  90.         if (nullptr == thread_ptr)
  91.         {
  92.             lockGuard lockguard(&mutex);
  93.             if (nullptr == thread_ptr)
  94.             {
  95.                 thread_ptr = new ThreadPool<T>(num);
  96.             }
  97.         }
  98.         return thread_ptr;
  99.     }
  100.     // 启动线程池中的线程
  101.     void run()
  102.     {
  103.         for (auto &iter : threads_)
  104.         {
  105.             iter->start();
  106.             std::cout << iter->name() << " 启动成功" << std::endl;
  107.         }
  108.     }
  109.     // 线程执行的函数
  110.     static void *routine(void *args)
  111.     {
  112.         ThreadData *td = (ThreadData *)args;
  113.         ThreadPool<T> *tp = (ThreadPool<T> *)td->args_;
  114.         while (true)
  115.         {
  116.             T task;
  117.             {
  118.                 lockGuard lockguard(tp->getMutex());
  119.                 while (tp->isEmpty())
  120.                     tp->waitCond();
  121.                 task = tp->getTask();
  122.             }
  123.             task(td->name_);
  124.         }
  125.     }
  126.     // 向任务队列中添加任务
  127.     void pushTask(const T &task)
  128.     {
  129.         lockGuard lockguard(&lock);
  130.         task_queue_.push(task);
  131.         pthread_cond_signal(&cond);
  132.     }
  133.     // 析构函数,销毁线程池
  134.     ~ThreadPool()
  135.     {
  136.         for (auto &iter : threads_)
  137.         {
  138.             iter->join();
  139.             delete iter;
  140.         }
  141.         pthread_mutex_destroy(&lock);
  142.         pthread_cond_destroy(&cond);
  143.     }
  144. private:
  145.     std::vector<Thread *> threads_; // 线程对象数组
  146.     int num_; // 线程数量
  147.     std::queue<T> task_queue_; // 任务队列
  148.     static ThreadPool<T> *thread_ptr; // 线程池单例指针
  149.     static pthread_mutex_t mutex; // 线程池单例的互斥锁
  150.     pthread_mutex_t lock; // 线程池内部使用的互斥锁
  151.     pthread_cond_t cond; // 线程池内部使用的条件变量
  152. };
  153. // 静态成员初始化
  154. template <typename T>
  155. ThreadPool<T> *ThreadPool<T>::thread_ptr = nullptr;
  156. template <typename T>
  157. pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;
复制代码
⭕ . cpp 文件

✅testMain.cpp

  1. #include "threadPool.hpp" // 包含线程池头文件
  2. #include "Task.hpp" // 包含任务类的头文件
  3. #include <ctime>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <unistd.h>
  7. int main()
  8. {
  9.     srand((unsigned long)time(nullptr) ^ getpid()); // 初始化随机数种子
  10.     // 获取线程池单例并运行线程
  11.     ThreadPool<Task>::getThreadPool()->run();
  12.     while(true)
  13.     {
  14.         // 生产任务的过程,制作任务的时候,要花时间
  15.         int x = rand() % 100 + 1; // 随机生成一个范围在1到100之间的整数x
  16.         usleep(7721); // 模拟制作任务需要的时间
  17.         int y = rand() % 30 + 1; // 随机生成一个范围在1到30之间的整数y
  18.         Task t(x, y, [](int x, int y) -> int { // 创建任务对象t,执行加法操作
  19.             return x + y;
  20.         });
  21.         std::cout << "制作任务完成: " << x << "+" << y << "=?" << std::endl; // 输出任务信息
  22.         // 推送任务到线程池中
  23.         ThreadPool<Task>::getThreadPool()->pushTask(t);
  24.         sleep(1); // 暂停一秒钟
  25.     }
  26.     return 0;
  27. }
复制代码
这段代码主要实现了一个简单的任务生产者,不断地天生任务并将任务推送到线程池中执行。
三、线程池的优点

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

  • 提高性能:线程池可以减少线程创建和销毁的开销,通过重用线程,克制了频仍地创建和销毁线程所带来的性能消耗。
  • 控制并发度:线程池可以限定同时执行的线程数量,从而控制系统的并发度,防止由于过多线程导致系统资源被耗尽。
  • 提高响应速度:由于线程池中的线程已经创建好并处于就绪状态,当任务到达时可以立即执行,从而减少了任务等待执行的时间,提高了系统的响应速度。
  • 简化线程管理:线程池封装了线程的创建、销毁、调理等操作,简化了线程管理的复杂性,提高了代码的可维护性。
  • 控制资源占用:线程池可以限定系统中同时存在的线程数量,从而控制系统对资源(如内存、CPU)的占用,防止资源被耗尽导致系统瓦解。
总而言之,线程池提供了一种高效、可控的线程管理机制,实用于处置惩罚大量并发任务的场景,是提高系统性能和响应速度的紧张工具之一。
温馨提示

感谢您对博主文章的关注与支持!假如您喜欢这篇文章,可以点赞、评论和分享给您的同砚,这将对我提供巨大的鼓励和支持。另外,我筹划在未来的更新中持续探讨与本文相干的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。假如感兴趣的话可以关注博主的更新,不要错过任何精彩内容!
再次感谢您的支持和关注。我们期待与您创建更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生存愉快,排便顺畅!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莱莱

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表