【linux】手搓线程池

ops/2024/11/29 1:40:33/

linux_0">【linux】进程池

  • 线程池:

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着 监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利 用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

线程池的应用场景:

  • 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技 术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个 Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
  • 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  • 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情 况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限, 出现错误.
#ifndef __THREAD_HPP__
#define __THREAD_HPP__
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <functional>
namespace ThreadModule
{using func_t = std::function<void(std::string)>;class Thread{public:Thread(func_t func, std::string threadname = "none_name") // 为什么--常量引用和非常量引用的概念: _func(func), _threadname(threadname), _stop(true){}void Excute(){_func(_threadname);}static void *handler(void *args){Thread *self = static_cast<Thread*>(args);self->Excute();return nullptr;}bool Start(){int ret = pthread_create(&_tid, nullptr, handler, this);if (ret == 0){_stop = false;return true;}else{return false;}}void Join(){if (!_stop){pthread_join(_tid, nullptr);}}std::string name(){return _threadname;}~Thread() {}private:std::string _threadname;pthread_t _tid;func_t _func;bool _stop;};
}
#endif
#ifndef __THREAD_POOL_HPP__
#define __THREAD_POOL_HPP__
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <vector>
#include "log.hpp"
#include <queue>
#include "Thread.hpp"
#include <functional>
using namespace ThreadModule;
const int def_thread_sum = 3; // 默认线程数量template <typename T>
class thread_pool
{
public:void Lock_queue(){pthread_mutex_lock(&_mutex);}void UnLock_queue(){pthread_mutex_unlock(&_mutex);}void Wait_thread(){pthread_cond_wait(&_cond, &_mutex);}void Wake_up_thread(){pthread_cond_signal(&_cond);}void Wake_up_allthread(){pthread_cond_broadcast(&_cond);}public:thread_pool(int threadsum = def_thread_sum): _threadsum(threadsum), _isruning(false), _waitsum(0){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}void HandlerTask(std::string name)// 类的成员方法,也可以成为另一个类的回调方法,方便我们继续类级别的互相调用!{while (true){// 1. 保证队列安全Lock_queue();// 当任务队列为空,线程运行时// 2. 队列中不一定有数据while (_Task_queue.empty() && _isruning) // 当任务队列为空,有线程在运行,每个被唤醒的线程都要重新判断条件{_waitsum++;    // 等待的线程数量加一Wait_thread(); // 将线程加入等待队列中,解锁--等待被唤醒--加锁_waitsum--;}// 2.1 如果线程池已经退出了 && 任务队列是空的if (_Task_queue.empty() && !_isruning){UnLock_queue();break;}// 2.2 如果线程池不退出 && 任务队列不是空的// 2.3 如果线程池已经退出 && 任务队列不是空的 --- 处理完所有的任务,然后在退出// 3. 一定有任务, 处理任务// 一定有任务T t = _Task_queue.front(); // 取出任务_Task_queue.pop();UnLock_queue();LOG(DEBUG, "%s get a task", name.c_str());//打印日志// 4. 处理任务,这个任务属于线程独占的任务t();// 执行任务LOG(DEBUG, "%s handler a task, result is: %s", name.c_str(), t.Result_tostring().c_str());}}void Init_thread_pool(){// 指向构建出所有的线程,并不启动for (int i = 0; i < _threadsum; i++){std::string name = "thread-" + std::to_string(i + 1);_thread_pool.emplace_back(std::bind(&thread_pool::HandlerTask, this, std::placeholders::_1), name);LOG(INFO, "init thread %s done", name.c_str());//打印日志}_isruning = true;}void Start_threads(){for (auto &thread : _thread_pool){thread.Start();}}bool Enqueue(T &in){bool ret = false;if (_isruning){Lock_queue();_Task_queue.push(in);if (_waitsum > 0) //等待队列中的线程数量大于0{Wake_up_thread();//唤醒等待队里中的一个线程}LOG(DEBUG, "enqueue task success");//打印日志ret = true;UnLock_queue();}return ret;}void WaitAllThread(){for (auto &thread : _thread_pool){thread.Join();//等待所有线程LOG(INFO, "%s is quit...", thread.name().c_str());//打印日志}}void Stop(){Lock_queue();_isruning = false;Wake_up_allthread();UnLock_queue();}~thread_pool(){pthread_mutex_destroy(&_mutex);//释放互斥量pthread_cond_destroy(&_cond);//释放条件变量}private:int _threadsum; //线程数量std::vector<Thread> _thread_pool;//线程池结构std::queue<T> _Task_queue;//任务队列pthread_mutex_t _mutex;//互斥量pthread_cond_t _cond;//条件变量bool _isruning;//正在运行的队列int _waitsum;//正在等待的线程的数量
};
#endif
#pragma once 
#include <iostream>
#include<string>class Task
{
public:Task(int a, int b) : _a(a), _b(b){}void Excute(){_result = _a + _b;}void operator()(){_result = _a + _b;}std::string Result_tostring(){return std::to_string(_a)+" + " +std::to_string(_b)+" = "+std::to_string(_result);}std::string Debug_tostring(){return std::to_string(_a)+" + "+std::to_string(_b)+" = ?";}~Task(){}private:int _a;int _b;int _result;
};
#pragma once
#include <iostream>
#include <stdarg.h>
#include <fstream>
#include "LockGuard.hpp"
const static char *logname = "log.txt";//日志文件
bool g_save = false; 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
enum level//日志的等级
{DEBUG = 0,INFO,WARNING,ERROR,FATAL
};
void save_file(const std::string &logname, std::string &massage)//保存日志到文件中
{std::ofstream infile("logname", std::ios::app);if (!infile.is_open()){return;}infile << massage << std::endl;infile.close();
}
// 获取日志等级
std::string get_level_string(int level)
{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "None";}
}
// 获取时间字符串
std::string get_time_string()
{time_t cur_time = time(nullptr);if (cur_time == (time_t)-1){printf("Failed to get the current time.\n");return "None";}struct tm *formate_time = localtime(&cur_time);if (formate_time == nullptr){return "None";}char buffer[1024];snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d",formate_time->tm_year + 1900,formate_time->tm_mon + 1,formate_time->tm_mday,formate_time->tm_hour,formate_time->tm_min,formate_time->tm_sec);return buffer;
}
// 日志信息
void  Log_inf(std::string filename, int line, bool is_save, int level, const char *format, ...)
{std::string levelstr = get_level_string(level);std::string time = get_time_string();pid_t selfid = getpid();char buffer[1024];va_list arg;va_start(arg, format);vsnprintf(buffer, sizeof(buffer), format, arg);va_end(arg);std::string massage = "[" + time + "]" + "[" + levelstr + "]" + "[" + std::to_string(selfid) + "]" + "[" + filename + "]" + "[" + std::to_string(line) + "]" + buffer + "\n";LockGuard lockguard(mutex); // RAIIif (is_save){// 保存到文件中save_file(logname, massage);}else{   //向屏幕中打印std::cout << massage;}
}// 定义宏
#define LOG(level, format, ...)                                            \do                                                                     \{                                                                      \Log_inf(__FILE__, __LINE__, g_save, level, format, ##__VA_ARGS__); \} while (0)
#define Enablefile()   \do                 \{                  \g_save = true; \} while (0)
#define EnableScrean()  \do                  \{                   \g_save = false; \} while (0)
#include <iostream>
#include "ThreadPool.hpp"
#include "Thread.hpp"
#include"Task.hpp"
#include<memory>
#include"log.hpp"
using namespace ThreadModule;
int main()
{Enablefile();std::unique_ptr<thread_pool<Task>> tp = std::make_unique<thread_pool<Task>>(5);tp->Init_thread_pool();tp->Start_threads();int cnt = 10;srand((signed)time(NULL));while(cnt--){int frist = rand()%10+1;usleep(1234);int second = rand()%10+1;Task t(frist,second);LOG(INFO,"main send Task is :%s", t.Debug_tostring().c_str());//打印日志tp->Enqueue(t);sleep(2);}tp->Stop();tp->WaitAllThread();LOG(INFO,"main thread is quit ... \n");//打印日志return 0;
}

[!IMPORTANT]

【Linux】线程篇完结!!,下一篇【Linux】网络篇


http://www.ppmy.cn/ops/137528.html

相关文章

替代Postman ,17.3K star!

现在&#xff0c;许多人都朝着全栈工程师的方向发展&#xff0c;API 接口的编写和调试已成为许多开发人员必备的技能之一。 工欲善其事&#xff0c;必先利其器。拥有一款优秀的 API 工具对于任何工程师来说都是极为重要的&#xff0c;它能够帮助我们高效地完成各种开发任务。 …

什么是JSON,有什么特点

什么是 JSON&#xff1f; JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解析和生成。它基于 JavaScript 的子集&#xff0c;但独立于语言&#xff0c;被广泛用于服务器与 Web 应…

Mybatis控制台打印SQL执行信息(执行方法、执行SQL、执行时间)

文章目录 前言一、基本功能介绍1.1本章功能效果预览图: 二、可执行源码2.1 yaml基础配置2.2 MybatisAnalyzeSQLInterceptor实现SQL拦截 前言 SQL性能监控是一个程序必要的功能&#xff0c;通常我们可以使用数据库自带的客户端工具进行SQL性能分析。然而对于一些专业度不高的人…

性能测试调优之线程池的性能优化

做性能测试时&#xff0c;有些压测场景下TPS上不去&#xff0c;或者响应时间变长&#xff0c;或者直接出现一些连接 被拒绝的报错&#xff0c;这些都有可能是tomcat的连接池不够引起的。 连接池的概念 线程池&#xff1a;是一个管理线程集合的框架&#xff0c;它负责维护一个…

【Oracle11g SQL详解】 SELECT 语句的基础用法与示例

SELECT 语句的基础用法与示例 在 Oracle 11g 中&#xff0c;SELECT 语句是最常用的 SQL 语句&#xff0c;用于从数据库表中查询数据。本文将从语法结构、使用方法和常见示例出发&#xff0c;系统讲解 SELECT 语句的基础用法。 一、SELECT 语句的基本语法 SELECT 列名1, 列名2…

大数据机器学习算法与计算机视觉应用05:乘法权重算法

The Multiplicative Weight Algorithm The Experts ProblemWeighed Majority AlgorithmLower Bound for Deterministic AlgorithmsRandomized Weighed Majority Algorithm The Experts Problem 假设现在有 n n n位专家对 T T T天的做出预测 在第 t t t天&#xff0c;第 i i …

后台管理-动态路由配置以及用户权限管理(vue3+element plus+koa+Sequelize )

前言 之前开发了一个校园二手物品交易网站的demo 前端采用Vue2结合Element UI 后端采用了koa 、Sequelize 、Mysql 在工作了一年多之后&#xff0c;突然想针对工作所学来完善一下自己手里的项目 想要做一个针对于该平台或多个平台&#xff0c;来进行路由配置和角色集中管理…

算法篇:贪心算法

题目一&#xff1a;均分纸牌 有n堆纸牌&#xff0c;编号分别为 1&#xff0c;2&#xff0c;…,n1&#xff0c;2&#xff0c;…,n。每堆上有若干张&#xff0c;但纸牌总数必为nn的倍数。可以在任一堆上取若干张纸牌&#xff0c;然后移动。 移牌规则为&#xff1a;在编号为11的…