【Linux】 线程池

news2024/9/21 12:35:04

线程池

什么是线程池?
一次预先申请一批线程,让这批线程有任务,就处理任务;没任务,就处于等待状态。
为什么要有线程池?
以空间换时间,预先申请一批线程,当有任务到来,可以直接指派给线程执行。

// task.hpp
#pragma once

#include <functional>

using namespace std;

typedef function<int(int, int)> calc_func_t;

class Task
{
public:
    Task() {}
    Task(int x, int y, calc_func_t func)
        : _x(x), _y(y), _calc_func(func)
    {}
    // 加法计算的任务
    int operator()() { return _calc_func(_x, _y); }
    int get_x() { return _x; }
    int get_y() { return _y; }
private:
    int _x;
    int _y;
    calc_func_t _calc_func;
};
// log.hpp
#pragma once

#include <string>
#include <stdarg.h>
#include <unordered_map>

using namespace std;

#define LOG_FILE "./threadpool.log"

// 日志是有日志级别的
enum LogLevel
{
    DEBUG,
    NORMAL,
    WARNING,
    ERROR,
    FATAL
};

// 针对枚举类型的哈希函数
template <typename T>
class EnumHash
{
public:
    size_t operator()(const T& t) const { return static_cast<size_t>(t); }
};
unordered_map<LogLevel, string, EnumHash<LogLevel>> logLevelMap = {
    {DEBUG, "DEBUG"},
    {NORMAL, "NORMAL"},
    {WARNING, "WARNING"},
    {ERROR , "ERROR"},
    {FATAL, "FATAL"}
};

// 完整的日志功能,至少有:日志等级 时间 支持用户自定义
void logMessage(LogLevel log_level, const char* format, ...)
{
#ifndef DEBUG_SHOW
    if(log_level == DEBUG) return; // DEBUG_SHOW没有定义,不展示DEBUG信息
#endif 
    char stdBuffer[1024]; // 标准部分
    char logBuffer[1024]; // 自定义部分
    
    time_t timestamp = time(nullptr);
    struct tm* ploct = localtime(&timestamp);
    snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%04d-%02d-%02d %02d:%02d:%02d]", logLevelMap[log_level].c_str(),\
    1900 + ploct->tm_year, 1 + ploct->tm_mon, ploct->tm_mday, ploct->tm_hour, ploct->tm_min, ploct->tm_sec);

    va_list args;
    va_start(args, format);
    vsnprintf(logBuffer, sizeof logBuffer, format, args);
    va_end(args);

    FILE* log_file = fopen(LOG_FILE, "a");
    fprintf(log_file, "%s %s\n", stdBuffer, logBuffer);
    fclose(log_file);
}

va_*系列函数与vprintf系列函数配合使用可以格式化打印传入的可变参数的内容。
在这里插入图片描述
在这里插入图片描述

// thread.hpp
#pragma once

#include <string>
#include <cstdio>
#include <pthread.h>

using namespace std;

// 对应创建线程时的routine函数的类型
typedef void*(*func_t)(void*);

class ThreadData
{
public:
    void* _ptpool; // 指向线程池对象
    string _name;
};

class Thread
{
public:
    Thread(int num, func_t callBack, void* _ptpool)
        : _func(callBack)
    {
        char nameBuffer[64];
        snprintf(nameBuffer, sizeof(nameBuffer), "Thread_%d", num);
        _tdata._name = nameBuffer;
        _tdata._ptpool = _ptpool;
    }

    void start() { pthread_create(&_tid, nullptr, _func, (void*)&_tdata); }

    void join() { pthread_join(_tid, nullptr); }

    const string& name() { return _tdata._name; }
private:
    pthread_t _tid; // 线程ID
    func_t _func; // 线程routine
    ThreadData _tdata; // 线程数据
};
// threadPool.hpp
#pragma once

#include <vector>
#include <queue>
#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

const int g_thread_num = 3;

// 线程池:本质是生产消费模型
template<class T>
class threadPool
{
private:
    threadPool(int thread_num = g_thread_num)
        : _thread_num(thread_num)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for(int i = 0; i < _thread_num; ++i)
        {
            _threads.push_back(new Thread(i + 1/*线程编号*/, routine, this/*可以传this指针*/));
        }
    }
    threadPool(const threadPool<T>&) = delete;
    const threadPool<T>& operator=(const threadPool<T>&) = delete;
public:
    // 考虑多个线程使用单例的情况
    static threadPool<T>* getThreadPool(int thread_num = g_thread_num)
    {
        if(nullptr == _pthread_pool)
        {
            lockGuard lock_guard(&_pool_lock);
            // 在单例创建好后,锁也就没有意义了
            // 将来任何一个线程要获取单例,仍必须调用getThreadPool接口
            // 这样一定会存在大量的申请和释放锁的行为
            // 所以外层if判断,用于在单例创建的情况下,拦截大量的线程因请求单例而访问锁的行为
            if(nullptr == _pthread_pool)
            {
                _pthread_pool = new threadPool<T>(thread_num);
            }
        }
        
        return _pthread_pool;
    }

    void run()
    {
        for(auto& pthread : _threads)
        {
            pthread->start();
            logMessage(NORMAL, "%s %s", (pthread->name()).c_str(), "启动成功");
        }
    }

    void pushTask(const T& task)
    {
        lockGuard lock_guard(&_lock);
        _task_queue.push(task);
        pthread_cond_signal(&_cond);
    }

    ~threadPool()
    {
        for(auto& pthread : _threads)
        {
            pthread->join();
            delete pthread;
        }
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }
public:
    pthread_mutex_t* getMutex()
    {
        return &_lock;
    }
    bool isEmpty()
    {
        return _task_queue.empty();
    }
    void waitCond()
    {
        pthread_cond_wait(&_cond, &_lock);
    }
    T& getTask()
    {
        T& task = _task_queue.front();
        _task_queue.pop();
        return task;
    }
private:
    // 消费过程
    static void* routine(void* args)
    {
        ThreadData* tdata = (ThreadData*)args;
        threadPool<T>* tpool = (threadPool<T>*)tdata->_ptpool;
        while(true)
        {
            T task;
            {
                lockGuard lock_guard(tpool->getMutex());
                while (tpool->isEmpty()) tpool->waitCond();
                task = tpool->getTask();
            }
            logMessage(WARNING, "%s 处理完成: %d + %d = %d", (tdata->_name).c_str(), task.get_x(), task.get_y(), task());
        }
    }
private:
    vector<Thread*> _threads; // 数组存放创建的线程的地址
    int _thread_num; // 创建的线程个数

    queue<T> _task_queue; // 阻塞式任务队列
    pthread_mutex_t _lock; // 针对任务队列的锁
    pthread_cond_t _cond; // 队列空满情况的条件变量

    static threadPool<T>* _pthread_pool; // 饿汉式线程池
    static pthread_mutex_t _pool_lock; // 针对线程池的锁
};

template<class T>
threadPool<T>* threadPool<T>::_pthread_pool = nullptr;
template<class T>
pthread_mutex_t threadPool<T>::_pool_lock = PTHREAD_MUTEX_INITIALIZER;
// test.cc
#include "task.hpp"
#include "threadPool.hpp"
#include <unistd.h>
#include <ctime>

void test1()
{
    srand((unsigned int)time(nullptr) ^ getpid());
    threadPool<Task>::getThreadPool()->run();

    while(true)
    {
        // 生产的过程 - 制作任务的时候要花时间的
        int x = rand() % 100 + 1;
        usleep(2023);
        int y = rand() % 50 + 1;
        Task task(x, y, [](int x, int y){ return x + y; });
        logMessage(DEBUG, "制作任务完成: %d + %d = ?", x, y);

        // 推送任务到线程池
        threadPool<Task>::getThreadPool()->pushTask(task);
        sleep(1);
    }
}
# Makefile
test:test.cc
	g++ -o $@ $^ -std=c++11 -lpthread -DDEBUG_SHOW
.PHONY:clean
clean:
	rm -f test

运行结果:
在这里插入图片描述

自旋锁

自旋锁:本质是通过不断检测锁的状态,来确定资源是否就绪的方案。
什么时候使用自旋锁?这个由临界资源就绪的时间长短决定。
自旋锁的初始化 & 销毁:
在这里插入图片描述
自旋锁的加锁:
在这里插入图片描述
自旋锁的解锁:
在这里插入图片描述

读者写者问题

写者与写者:互斥关系
读者与写者:互斥 & 同步关系
读者与读者:共享关系

读者写者问题和生产消费模型的本质区别在于,消费者会拿走数据(做修改),而读者不会。
读写锁的初始化 & 销毁:
在这里插入图片描述
读写锁之读者加锁:
在这里插入图片描述
读写锁之写者加锁:
在这里插入图片描述
读写锁的解锁:
在这里插入图片描述
关于是读者还是写者优先的问题,抛开应用场景去谈技术细节就是耍流氓。
而pthread库中的读写锁默认采用读者优先,这类的应用场景主要是:数据被读取的频率非常高,被修改的频率非常低。

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

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

相关文章

arkts编译报错-arkts-limited-stdlib错误【Bug已完美解决-鸿蒙开发】

文章目录 项目场景:问题描述原因分析:解决方案:适配指导案例此Bug解决方案总结项目场景: arkts编译报错-arkts-limited-stdlib错误。 我用Deveco studio4.0 beta2开发应用,报arkts-limited-stdlib错误 报错内容为: ERROR: ArKTS:ERROR File: D:/prRevivw/3792lapplica…

基于深度学习的yolov5入侵检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介IntroductionYOLOv5 Overview入侵检测系统架构1. 数据采集2. YOLOv5模型训练3. 实时监测4. 告警与反馈 性能评估与优化 二、功能三、系统四. 总结 一项目简…

渗透测试是什么

随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显。其中&#xff0c;渗透测试作为一种重要的安全评估方法&#xff0c;已经被越来越多的企业和组织所采用。渗透测试通过模拟黑客攻击&#xff0c;发现并修复潜在的安全漏洞&#xff0c;从而提高系统的安全性。 直白的说…

【数据安全】金融行业数据安全保障措施汇总

数字化的今天&#xff0c;数据的价值不可估量&#xff0c;尤其是金融行业&#xff0c;数据不仅代表着企业的核心资产&#xff0c;还涉及到客户的隐私和信任。因此对于金融行业而言&#xff0c;保障数据安全至关重要。下面我们就来一起讨论为什么金融行业要保障数据安全&#xf…

功能介绍 | 红队企业版全新功能定时扫描与企业版报告强势来袭!

0x01 前言 面对大型企业复杂且庞大的网络环境&#xff0c;我们通常会遇到资产繁杂、边缘资产发现困难以及资产种类梳理不全等问题。Goby红队企业版的出现&#xff0c;旨在除了满足企业在漏洞扫描、威胁情报分析、安全评估等方面的基础需求之外&#xff0c;我们也在一直持续更新…

12.5单端口RAM,JS计数器,流水线乘法器,不重叠序列检测器(状态机+移位寄存器),信号发生器,交通灯

单端口RAM timescale 1ns/1nsmodule RAM_1port(input clk,input rst,input enb,input [6:0]addr,input [3:0]w_data,output wire [3:0]r_data );reg [6:0]mem[127:0];integer i;always (posedge clk or negedge rst) beginif(!rst) beginfor (i0; i<127 ; ii1) beginmem[i]…

Python3 中常见的数据类型

目录 数字(Number)总结 字符串(String)字符串运算符字符串格式化字符串的截取总结 List&#xff08;列表&#xff09;更新列表删除列表元素列表函数&方法总结 Tuple&#xff08;元组&#xff09;修改元组删除元组总结 Set&#xff08;集合&#xff09;Dictionary&#xff0…

Java实现快速排序算法

快速排序算法 &#xff08;1&#xff09;概念&#xff1a;快速排序是指通过一趟排序将要排序的数据分割成独立的两部分&#xff0c;其中一部分的所有数据都比另外一部分的所有数据都要小&#xff0c;然后再按此方法对这两部分数据分别进行快速排序。整个排序过程可以递归进行&…

Python中的魔力编程:掌握面向对象之道

Python中的面向对象编程 背景&#xff1a; ​ 最近在看一些代码的时候&#xff0c;对类中的一些内置方法不是很懂&#xff0c;因此出一篇文章来细说一下&#xff0c;希望大家看完后对Python中类有一个清楚的认识。 基础铺垫&#xff1a; ​ 面向对象的三个特点&#xff1a;…

案例课6——追一科技

1.公司介绍 追一科技是一家企业级智能服务AI公司&#xff0c;创立于2016年3月&#xff0c;主攻深度学习和自然语言处理&#xff0c;为金融、零售、生活服务等领域企业提供智能服务系统和解决方案。 追一科技的智能服务系统AIforce&#xff0c;拥有AI语义理解能力、智能产品矩阵…

【Android】完美解决Cannot resolve method ‘subscribe(Observer<T>)‘

问题截图&#xff1a; 解决方法&#xff1a; 如上图&#xff0c;看我标123的三个地方&#xff0c;2标注的地方提示我们我方法实际返回的值是Observer<Res_GetCellCode>,而我想要返回的结果是&#xff1a;3标记的结果&#xff1a;Observer<Res_QueryCTInfo>&#xf…

python自动化运维快速入门,python自动化运维教程

大家好&#xff0c;给大家分享一下python自动化运维需要掌握的技能&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 面向学员 熟练使用计算机&#xff0c;对Windows、Linux 有一点了解从业职或在校学生 对目前从事互联网运维&#xff0c;想…

js 生成分享码或分享口令

代码 function getShareToken(length) {var characters ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789;var shareToken ;for (var i 0; i < length; i) {var randomIndex Math.floor(Math.random() * characters.length);var randomChar character…

亚马逊云科技Amazon Bedrock,现推出更多模型选择和全新强大功能

亚马逊云科技在re:Invent 2023上宣布推出Amazon Bedrock更多模型选择和强大功能&#xff0c;帮助客户更轻松地构建和规模化针对其业务定制的生成式AI应用程序。 Amazon Bedrock是一项全面托管的服务&#xff0c;用户可轻松访问来自AI21 Labs、Anthropic、Cohere、Meta、Stabili…

CRM的作用:强化客户忠诚度和提升业务效益

随着国内市场的不断发展和企业数字化进程持续进行&#xff0c;许多人在工作和生活中或多或少都对CRM客户关系管理系统有所耳闻&#xff0c;但可能并不清楚CRM管理系统具体是什么&#xff0c;以及都有什么作用。这篇文章带您全面了解一下&#xff0c;CRM是什么&#xff0c;以及C…

java-两个列表进行比较,判断那些是需要新增的、删除的、和更新的

文章目录 前言两个列表进行比较&#xff0c;判断那些是需要新增的、删除的、和更新的 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实…

02基于matlab的卡尔曼滤波

基于matlab的卡尔曼滤波&#xff0c;可更改状态转移方程&#xff0c;控制输入&#xff0c;观测方程&#xff0c;设置生成的信号的噪声标准差&#xff0c;设置状态转移方差Q和观测方差R等参数&#xff0c;程序已调通&#xff0c;需要直接拍下。

bootstrap:下拉菜单

<!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>下拉菜单DEMO</title> <link rel"stylesheet" type"text/css" href"/cdn.bootcss.com/bootstrap/3.3.2/css/bootstrap.min.css"…

加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /)

文章目录 &#x1f680;前言&#x1f680;异或运算以及与运算&#x1f680;加法的实现&#x1f680;减法的实现&#x1f680;乘法的实现&#x1f680;除法的实现 &#x1f680;前言 这也是阿辉开的新专栏&#xff0c;知识将会很零散不成体系&#xff0c;不过绝对干货满满&…

螺旋方阵-2d

Description 一个 n 行 n 列的螺旋方阵按如下方法生成&#xff1a;从方阵的左上角&#xff08;第 1 行第 1 列&#xff09;出发&#xff0c;初始时向右移动&#xff1b;如果前方是未曾经过的格子&#xff0c;则继续前进&#xff1b;否则&#xff0c;右转。重复上述操作直至经过…