Linux学习之路 -- 线程 -- 线程池

news2024/11/16 9:25:10

前面介绍了条件变量的生产消费模型,下面介绍一下条件变量的另一个用法,那就是线程池。线程池的用法其实就是先创建一批线程,然后让这些线程从任务队列中取数据。具体就是生产消费者模型,(我的代码中生产线程只有一个并且生产的任务比较简单,如果有需要可以自行设计添加)

目录

1、示例代码

<1>ThreadModule.hpp

<2>Threadpool.hpp

<3>Log.hpp

<4>Task.hpp

<5>Main.cc

2、日志

<1>日志等级

<2>日志时间

<3>日志内容

<4>日志宏

<5>日志打印


1、示例代码

<1>ThreadModule.hpp

#pragma once

#ifndef __THREAD_HPP__
#define __THREAD_HPP__

#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>

namespace ThreadModule
{
    using func_t = std::function<void(std::string name)>;
    // typedef std::function<void(const T&)> func_t;

    class Thread
    {
    public:
        void Excute()
        {
            _func(_threadname);
        }
    public:
        Thread(func_t func, const std::string name="none-name")//右值
            : _func(func),  _threadname(name), _stop(true)
        {}
        static void *threadroutine(void *args) // 类成员函数,形参是有this指针的!!
        {
            Thread *self = static_cast<Thread *>(args);
            self->Excute();
            return nullptr;
        }
        bool Start()
        {
            int n = pthread_create(&_tid, nullptr, threadroutine, this);
            if(!n)
            {
                _stop = false;
                return true;
            }
            else
            {
                return false;
            }
        }
        void Detach()
        {
            if(!_stop)
            {
                pthread_detach(_tid);
            }
        }
        void Join()
        {
            if(!_stop)
            {
                pthread_join(_tid, nullptr);
            }
        }
        std::string name()
        {
            return _threadname;
        }
        void Stop()
        {
            _stop = true;
        }

        ~Thread() {}

    private:
        pthread_t _tid;
        std::string _threadname; 
        func_t _func;
        bool _stop;
    };
}

#endif



<2>Threadpool.hpp

#pragma once
#include <iostream>
#include <queue>
#include <vector>
#include "pthread.h"
#include "Log.hpp"
#include "unistd.h"
#include "ThreadMode.hpp"

using namespace ThreadModule;

static int pthread_num = 4;

template <class T>
class Threadpool
{
private:
    void Lock()
    {
        pthread_mutex_lock(&glock);
    }
    void Unlock()
    {
        pthread_mutex_unlock(&glock);
    }
    void Thread_Sleep()
    {
        pthread_cond_wait(&gcond, &glock);
    }
    void Thread_wakeup()
    {
        pthread_cond_signal(&gcond);
    }
    void Thread_wakeAll()
    {
        pthread_cond_broadcast(&gcond);
    }

public:
    Threadpool(int cap = pthread_num) : _cap(cap), _isrunning(false)
    {
        pthread_mutex_init(&glock, nullptr);
        pthread_cond_init(&gcond, nullptr);
    }
    void Handler_Task(std::string threadname)
    {
        while (true)
        {
            Lock();
            // 任务队列为空,并且在运行
            while (_task_pool.empty() && _isrunning)
            {
                _wait_num++;
                Thread_Sleep();
                LOG(INFO, "%s wake up",threadname.c_str())
                _wait_num--;
            }

            // 任务队列为空,并且不运行了
            if (_task_pool.empty() && !_isrunning)
            {
                Unlock();
                break;
            }
            // 任务队列不为空
            LOG(DEBUG,"%s gain task", threadname.c_str())
            T t = _task_pool.front();
            _task_pool.pop();

            Unlock();
            t();
            LOG(INFO, "task done,result is %s", t.Result().c_str())
            sleep(1);
            // 执行自己的任务
        }
    }
    void Stop()
    {
        Lock();
        _isrunning = false;
        Thread_wakeAll();
        Unlock();
    }
    void Enqueue(const T &task)
    {
        Lock();
        _task_pool.push(task);
        if(_wait_num > 0)//如果有线程在等,直接唤醒线程
        {
            Thread_wakeup();
        }
        Unlock();
    }
    void Init_Threadpool()
    {
        for (int i = 0; i < _cap; i++)
        {
            std::string name = "thread - " + std::to_string(i + 1);
            _thread_pool.emplace_back(Thread(std::bind(&Threadpool::Handler_Task, this, std::placeholders::_1), name));
            LOG(INFO, "%s init success..." , name.c_str())
        }
        _isrunning = true;
    }
    void Start()
    {
        for (auto &e : _thread_pool)
        {
            LOG(DEBUG,"%s is running..." , e.name().c_str())
            e.Start();
        }
    }
    void Join()
    {
        for (auto &e : _thread_pool)
        {
            usleep(1500);
            LOG(DEBUG, "%s is quiting...", e.name().c_str())
            e.Join();
        }
    }

    ~Threadpool()
    {
        pthread_mutex_destroy(&glock);
        pthread_cond_destroy(&gcond);
    }

private:
    std::vector<Thread> _thread_pool;
    std::queue<T> _task_pool; // 任务队列
    int _cap;                 // 线程数量

    pthread_mutex_t glock;
    pthread_cond_t gcond;
    // 等待线程数
    int _wait_num;

    bool _isrunning; // 线程池是否在跑
};

<3>Log.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstdio>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>
#include <pthread.h>
#include <fstream>
enum Level
{
    INFO = 0,
    DEBUG,
    WARNING,
    ERROR,
    FATAL

};
std::string Level_tostring(int level)
{
    switch (level)
    {
    case INFO:
        return "INFO";
    case DEBUG:
        return "DEBUG";
    case WARNING:
        return "ERROR";
    case ERROR:
        return "ERROR";
    case FATAL:
        return "FATAL";
    default:
        return "Unkown";
    }
}

pthread_mutex_t _glock = PTHREAD_MUTEX_INITIALIZER;
bool _is_save = false;
const std::string filename = "log.txt";

void SaveLog(const std::string context)
{
    std::ofstream infile;
    infile.open(filename,std::ios::app);
    if(!infile.is_open())
    {
        std::cout << "open file failed" << std::endl;
    }
    else
    {
        infile << context;
    }
    infile.close();
}
std::string Gettime()
{
    time_t cur_time = time(NULL);
    struct tm *time_data = localtime(&cur_time);
    if (time_data == nullptr)
    {
        return "None";
    }
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d",
             time_data->tm_year + 1900,
             time_data->tm_mon + 1,
             time_data->tm_mday,
             time_data->tm_hour,
             time_data->tm_min,
             time_data->tm_sec);
    return buffer;
}
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{
    std::string levelstr = Level_tostring(level);
    std::string time = Gettime();
    // 可变参数
    char buffer[1024];
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);

    std::string context = "[" + levelstr + "]" + "[" + time + "]" + "[" + "line : " + std::to_string(line) + "]" + "[" + filename + "]" + ": " + buffer;
    pthread_mutex_lock(&_glock);
    if(!issave)
    {
        std::cout << context << std::endl;
    }
    else{
        SaveLog(context);
    }
    pthread_mutex_unlock(&_glock);
}

#define LOG(level, format, ...)                                          \
    do                                                                   \
    {                                                                    \
        LogMessage(__FILE__, __LINE__, _is_save, level, format, ##__VA_ARGS__); \
    } while (0);
#define EnableFile()    \
    do                  \
    {                   \
        _is_save = true; \
    } while (0);
#define EnableScreen()   \
    do                   \
    {                    \
        _is_save = false;\
    } while (0);

<4>Task.hpp

#pragma once
#include <iostream>

using namespace std;
class task
{
public:
    task(int a, int b) : _a(a), _b(b)
    {
    }
    std::string Result()
    {
        return to_string(_a) + "+" + to_string(_b) + "=" + to_string(_result);
    }
    void Excute()
    {
        _result = _a + _b;
    }
    void operator()()
    {
        Excute();
    }
    ~task()
    {}

private:
    int _a;
    int _b;
    int _result;
};

<5>Main.cc

#include <iostream>
#include "Threadpool.hpp"
#include "Log.hpp"
#include <memory>
#include <time.h>
#include <stdlib.h>
#include "Task.hpp"

int main()
{
    EnableScreen();
    std::unique_ptr<Threadpool<task>> pool = std::make_unique<Threadpool<task>>(5);//c++14语法
    pool->Init_Threadpool();
    pool->Start();
    srand(time(NULL) ^ getpid() ^ pthread_self());
    int tasknum = 10;
    while (tasknum--)
    {

        int a = rand() % 4 + 2;
        int b = rand() % 3 + 5;
        usleep(1200);
        pool->Enqueue(task(a, b));
        LOG(INFO, "task has pushed into queue,a is %d, b is %d", a,b)
    }

    pool->Stop();
    pool->Join();

    return 0;
}

2、日志

下面着重介绍一下日志的编写,线程池的其余部分都是生产消费模型的代码基础之上改进而来,代码量也不大,所以不详细介绍。日志是较为特殊,所以这里单独介绍一下。

<1>日志等级

日志实际上是需要分等级的,用于标明当前代码的执行情况。这里我设置五个等级

从上到下依次,正常,测试,警告,错误,致命。这几个等级表明了日志的级别,在打印日志时,我们需要手动设定(在示例代码中)

<2>日志时间

日志的时间也是日志的一项重要数据,所以这里就涉及了当前时间的获取。我们可以先设置当前时间的时间戳类型time_t变量,然后通过localtime函数转成当地时间(不用担心时区问题,这里会自行设置),头文件均是time.h

我们将设定的time_t的变量取地址传入time函数中,由于该函数的参数是输出型参数,所以time_t变量的值就变成了当前时间戳,再将该变量传入localtime函数中,此时函数就会返回一个struct tm* 类型的指针,该结构体的成员如下图。

该结构体会自行将时间戳转换成当前时间。需要注意的是,这里换算的月份会少一个月,年数会少1900年,所以我们在最后输出时需要注意加上去。

<3>日志内容

由于日志内容是不确定,所以这里我们需要使用可变参数进行调整。下面介绍一下如何提取可变参数中的内容。

首先我们需要先定义一个va_list 类型的变量,然后再用va_start对该变量进行初始化,初始化完毕后再一个一个可变参数中的内容。提取完成后,再用va_end对va_list类型的变量进行清除。

 va_start 有两个参数,第一个是我们定义的va_list变量,第二个就是在可变参数前的最后一个参数,也就是"..."前面一个参数。

然后我们可以使用va_arg对va_list列表中的内容进行提取,第一个参数还是va_list变量,第二个参数是需要提取的数据类型(假设要提取的是int,就填int)。提取一个该类型的数据后,该函数就会返回提取的数据(带类型的)。

va_end只有一个参数,我们传入开始定义的va_list变量,对其进行清空即可。

虽然上述的方法能够提取可变参数中的数据,但是我们最终的目的就是将可变参数的数据变成字符串打印到显示器上。所以我们这里还可以使用别的方式,也就是vsnprintf。

这几个函数的功能都差不过,这里不详细介绍,我们介绍完vsnprintf,其他的也就好理解了。这个vsprintf其实就是将va_list变量中的可变参数数据按照特定格式写入到大小为size的str缓冲区中。这里的format是可变参数前的最后一个数据(也就是...前的最后一个参数)。

<4>日志宏

如果我们打日志时,都需要在不断调用函数,那样太累了。所以这里我们可以使用宏来对函数进行替换。

这里我们用LOG替换LogMessage函数,其中__FILE__会自动显示当前日志函数所在的文件名称,__LINE__会自动显示当前日志函数所在的行数(_is_save稍后介绍),我们在使用宏时,只需传入level,和可变参数部分即可。然后需要注意的是,宏替换的LogMessage的可变参数部分不能也是...,我们必须使用__VA_ARGS__来表示,其中在该宏之前,我们还要加上##,否则在可变参数部分为零时,会出现问题。最后建议这里用do{} while(0)结构对函数进行包装,这样我们在使用宏替换时,替换的是一整个代码块,不容易出现问题。

<5>日志打印

日志不仅要能向显示器中打印,还得能向文件中打印。其中,上图的_is_save参数就是用于表示向哪里打印。该变量我设置成了全局变量,通过对其修改,就可以实现向不同的地方打印。(这里还有别的方式,如有需要,自行补充)

在这里,我设置了两个宏,用于调整_is_save的值,如果_is_save为false就是向显示器打印,为true就向文件中打印。在LogMessage函数内部会对该变量进行判断后决定向哪里打印。

注:打印部分最好加锁,显示器和文件在线程池这里也是临界资源

 以上就是所有内容,文中如有不对之处,还望指出,谢谢!!!

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

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

相关文章

自动微分-梯度!

前言背景知识&#xff1a; 梯度下降(Gradient descent,GD) 正文&#xff1a; 自动微分为机器学习、深度学习神经网络的核心知识之一&#xff0c;若想更深一步使用神经网络进行具体问题研究&#xff0c;那么自动微分不得不了解。 “工欲善其事&#xff0c;必先利其器”&…

数据结构 ——— 单链表oj题:合并两个升序链表

目录 题目要求 手搓两个简易链表 代码实现 题目要求 将两个升序链表合并为一个新的升序链表并返回&#xff0c;新链表是通过拼接给定的两个链表的所有节点组成的 手搓两个简易链表 代码演示&#xff1a; struct ListNode* n1 (struct ListNode*)malloc(sizeof(struct …

【Linux】第一个小程序——进度条实现

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…

【Python报错已解决】TypeError: ‘NoneType‘ object is not iterable

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

Android KMP 快速入门1 - 项目打包

这里写目录标题 KMP 运行与打包运行程序程序打包 KMP 运行与打包 运行程序 运行Android客户端&#xff0c;你首先需要把USB连接到物理机上&#xff0c;或者使用模拟器模拟一个手机&#xff1b; 然后选择运行配置的 composeApp &#xff0c;运行它即可 运行windows客户端&…

Qt/C++开源控件 自定义雷达控件

使用Qt框架创建一个简单的雷达图&#xff0c;包含动态扫描、目标点生成、刻度和方向标识。代码实现使用C编写&#xff0c;适合用作学习和扩展的基础。 1. 头文件与基本设置 #include "RadarWidget.h" #include <QPainter> #include <QPen> #include &…

解决银河麒麟操作系统V10软件包架构不符问题

TOC &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在银河麒麟桌面操作系统V10中安装软件包时&#xff0c;如果遇到“软件架构与本机架构不符”的提示&#xff0c;可以尝试以下步骤来解决问题&#xff1a; 1. 确认架构一致性 查看本机架构…

基于STM32的智能门禁系统

目录 引言项目背景环境准备 硬件准备软件安装与配置系统设计 系统架构关键技术代码示例 密码验证模块电机控制实现门禁状态监控应用场景结论 1. 引言 智能门禁系统能够通过密码或其他验证方式&#xff08;如指纹、刷卡等&#xff09;控制门的开关&#xff0c;结合电机控制与…

2024年7月大众点评武汉餐饮美食店铺基础信息

在做一些城市分析、学术研究分析、商业选址、商业布局分析等数据分析挖掘时&#xff0c;大众点评的数据参考价值非常大&#xff0c;截至2024年7月&#xff0c;大众点评美食店铺剔除了暂停营业、停止营业后的最新数据情况分析如下。 武汉餐饮美食店铺约9.6万家&#xff0c;有均…

MySQL高阶2051-商店中每个成员的级别

目录 题目 准备数据 分析数据 实现 总结 题目 一个商店想对其成员进行分类。有三个层次: "钻石": 如果转换率 大于或等于 80."黄金": 如果转换率 大于或等于 50 且小于 80."白银": 如果转化率 小于 50."青铜": 如果该成员从未访…

澳洲本科毕业论文的初稿撰写要点分析

临近毕业季的时候&#xff0c;如何更好地完成澳洲本科毕业论文成为了困扰大家的一大难题。澳洲毕业论文的质量高低关系到留学生能否顺利毕业。因此大家都会关心如何更好地完成毕业论文。我们在之前一些文章中介绍了如何确立论点&#xff0c;如何查找资料以及如何完成高质量的di…

HarmonyOS/OpenHarmony 如何将rawfile中文件复制到沙箱中

关键词&#xff1a;h5离线加载、HarmonyOS、OpenHarmony、文件操作、复制、解压 当下有一个场景&#xff0c;需要离线加载 h5离线资源zip包&#xff0c;并实现资源包的动态更新&#xff0c;那么仅靠 $rawfile并不能实现该功能&#xff0c;那么我们该如何实现&#xff1f; 我们…

面试题05.08绘制直线问题详解(考察点为位运算符)

目录 一题目&#xff1a; 二详细思路汇总&#xff1a; 三代码解答&#xff08;带注释版&#xff09;&#xff1a; 一题目&#xff1a; leetcode原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 二详细思路汇总&#xff1a; 这里先剧透一下简单版思路哦&…

Azure DevOps Server:不能指派新增的用户

Contents 1. 概述2. 解决方案 1. 概述 近期和微软Azure DevOps项目组解决了一个“无法指派开发人员”的问题&#xff0c;在此分享给大家。问题描述&#xff1a; 在一个数据量比较大的Azure DevOps Server的部署环境中&#xff0c;用户发现将新用户的AD域账户添加到Azure DevOps…

睢宁自闭症寄宿学校:培养特殊孩子的未来

在自闭症儿童的教育与康复领域&#xff0c;每一所学校的努力都是对孩子们未来无限可能的一次深刻诠释。从江苏睢宁到广东广州&#xff0c;自闭症寄宿学校正以不同的方式&#xff0c;为这些特殊的孩子铺设一条通往未来的希望之路。其中&#xff0c;广州的星贝育园自闭症儿童寄宿…

数据分析-29-基于pandas的窗口操作和对JSON格式数据的处理

文章目录 1 窗口操作1.1 滑动窗口思想1.2 函数df.rolling2 JSON格式数据2.1 处理简单JSON对象和JSON列表2.1.1 处理简单的JSON结构2.1.2 处理空字段2.1.3 获取部分字段2.2 处理多级json2.2.1 展开所有级别(默认)2.2.2 自定义展开层级2.3 处理嵌套列表JSON3 参考附录1 窗口操作 …

三维激光扫描技术在文保修缮项目中的应用

三维激光扫描技术作为一种新兴的高精度空间数据获取手段&#xff0c;其在文物保护和修缮项目中的应用日益广泛。这项技术通过快速获取物体表面的三维密集点云数据&#xff0c;为文物的数字化存档、保护、修复及再利用提供了强有力的技术支持。 数据采集&#xff1a;高精度与非接…

正则表达式(补充)

一、常见匹配模式 模式描述\w匹配字母数字及下划线\W匹配非字母数字下划线\s匹配任意空白字符&#xff0c;等价于 [\t\n\r\f].\S匹配任意非空字符\d匹配任意数字&#xff0c;等价于 [0-9]\D匹配任意非数字\A匹配字符串开始\Z匹配字符串结束&#xff0c;如果是存在换行&#xf…

[含文档+PPT+源码等]精品大数据项目-Django基于随机森林实现的空气质量指数预测研究系统

大数据项目-Django基于随机森林实现的空气质量指数预测研究系统的背景可以从以下几个方面进行阐述&#xff1a; 一、环境背景 空气污染问题日益严重&#xff1a; 随着工业化和城市化的快速发展&#xff0c;空气污染问题已成为全球性的挑战。空气中的主要污染物如PM2.5、PM10、…

DC00025【含论文】基于协同过滤推荐算法springboot视频推荐管理系统

1、项目功能演示 DC00025【含文档】基于springboot短视频推荐管理系统协同过滤算法视频推荐系统javaweb开发程序设计vue 2、项目功能描述 短视频推荐系统分为用户和系统管理员两个角色 2.1 用户角色 1、用户登录、用户注册 2、视频中心&#xff1a;信息查看、视频收藏、点赞、…