_Linux (线程池)

news2024/10/3 4:40:26

文章目录

  • 线程池概述:
  • 线程池示例:
    • 代码细节
    • 代码
    • 结果展示

线程池概述:

一种线程使用模式。

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

线程池的应用场景:

    1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
    1. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
    1. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误.

线程池示例:

    1. 创建固定数量线程池,循环从任务队列中获取任务对象,
    1. 获取到任务对象后,执行任务对象中的任务接口

代码细节

  • 线程创建和等待封装
    在这里插入图片描述
  • 日志封装
    • to_string: 可以把数字转换为字符串
    • _c++11(可变参数模板)
      在这里插入图片描述
  • 内存池–执行任务
  • 执行任务我们知道创建线程时需要传void*类型的函数(我们执行任务)它只有一个参数;我们需要在类内设置成静态函数(去掉this指针);而内存池的this的this指针提高args传参
int pthread_create(pthread_t *__restrict__ __newthread, const pthread_attr_t *__restrict__ __attr, void *(*__start_routine)(void *), void *__restrict__ __arg)

在这里插入图片描述

  • 类内成员变量访问我们通过类成员函数就可以了
    在这里插入图片描述

  • 执行任务和发布任务(线程池本质也是一个生产消费模型)我们此次需要条件变量和线程锁来保证线程安全(同步与互斥关系)

  • 锁封装时保证这一把锁是一个就可以了(指针或者引用都可以)
    在这里插入图片描述

代码

  • 线程锁封装

LockGuard.hpp

#pragma once
//  线程锁封装
#include <iostream>
#include <pthread.h>

class Mutex
{
public:
    Mutex(pthread_mutex_t* mtx):_mtx(mtx)
    {}
    void lock()
    {
        //std::cout << "要进行加锁" << std::endl;
        pthread_mutex_lock(_mtx);
    }
    void unlock()
    {
        //std::cout << "要进行解锁" << std::endl;
        pthread_mutex_unlock(_mtx);
    }
    ~Mutex()
    {}
public:
    pthread_mutex_t* _mtx;
};

// RAII风格的加锁方式
class LockGuard
{
public:
    LockGuard(pthread_mutex_t* mtx):_mx(mtx)
    {
        _mx.lock();
    }
    ~LockGuard()
    {
        _mx.unlock();
    }

private:
    Mutex _mx;    
};
  • 日志
    Log.hpp
#pragma once

#include <iostream>
#include <cstdio>
#include <cstdarg>
#include <ctime>
#include <string>

using std::cout;
using std::endl;
using std::string;

// 日志是有日志级别的
#define DEBUG 0   //  调试
#define NORMAL 1  //  正常
#define WARNING 2 //  警告
#define ERROR 3   //  错误
#define FATAL 4   //  致命错误

static const size_t BUFFER_NUM = 1024;
const char *gLevelMap[] = {
    "DEBUG",
    "NORMAL",
    "WARNING",
    "ERROR",
    "FATAL"};

// 完整的日志功能,至少: 日志等级 时间 支持用户自定义(日志内容, 文件行,文件名)
void LogMessage(int level, const char *format, ...)
{
    char stdBuffer[BUFFER_NUM]; // 标准部分
    const time_t timestamp = time(nullptr);
    struct tm *L_Time = localtime(&timestamp);
    string time;
    time += "日期-时间:" + std::to_string(L_Time->tm_year+1900) + "/" + std::to_string(L_Time->tm_mon) + "/" + std::to_string(L_Time->tm_mday) + "-" + std::to_string(L_Time->tm_hour) + ":" + std::to_string(L_Time->tm_min) + ":" + std::to_string(L_Time->tm_sec);
    std::to_string(L_Time->tm_sec);
    snprintf(stdBuffer, sizeof stdBuffer, "[%s][%s]",
             gLevelMap[level], time.c_str());

    char logBuffer[BUFFER_NUM]; // 自定义部分
    va_list args;
    va_start(args, format);
    vsnprintf(logBuffer, sizeof logBuffer, format, args);
    va_end(args);

    printf("%s%s\n", stdBuffer, logBuffer);
}
  • 任务(封装)
    Task.hpp
#pragma once
//  制作任务(封装)

#include "thread.hpp"
#include "Log.hpp"
using func_t = std::function<int(int, int)>;

class Task
{
public:
    Task() {}                                                   // 便于获取任务
    Task(int x, int y, func_t func) : _x(x), _y(y), _func(func) // 制作任务
    {
    }

    int operator()(string name) // 仿函数
    {
        //cout << "名字:" << name << "结果:" << _x << '*' << _y << '=' << _func(_x, _y) << endl;
    LogMessage(NORMAL, "%s处理完成: %d*%d=%d | %s | %d",
            name.c_str(), _x, _y, _func(_x, _y), __FILE__, __LINE__);
    }

public:
    int _x;
    int _y;
    func_t _func;
};
  • 多线程封装
    thread.hpp
#pragma once

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

using std::cout;
using std::endl;
using std::string;

static const size_t NAME_NUM = 1024;
typedef void*(*fun_t)(void*);   

class ThreadDate
{
public:
    void *_args;
    string _name;
};

class Thread
{
public:
    Thread(int num, fun_t callback, void *args) : _func(callback)
    {
        char nameBuffer[NAME_NUM];
        snprintf(nameBuffer, sizeof nameBuffer, "Thread-%d", num);

        _tdata._name = nameBuffer;
        _tdata._args = args;
    }
    void start() //  创造线程
    {
        pthread_create(&_tid, nullptr, _func, (void *)&_tdata);
    }
    void join() //  等待线程
    {
        pthread_join(_tid, nullptr);
    }
    string &name() //  线程名字
    {
        return _tdata._name;
    }

private:
    fun_t _func;
    ThreadDate _tdata;
    pthread_t _tid;
};
  • 线程池封装
    threadPool.hpp
#pragma once

#include <vector>
#include <queue>
#include <thread>
#include "thread.hpp"
#include "LockGuard.hpp"
#include "Log.hpp"

static const size_t g_thread_num = 4;
template <class T>
class ThreadPool
{
public:
    pthread_mutex_t *getMutex()
    {
        return &_lock;
    }
    bool isEmpty()
    {
        return _taskQueue.empty();
    }
    void waitCond()
    {
        pthread_cond_wait(&_cond, &_lock);
    }
    T getTask()
    {
        T t = _taskQueue.front();
        _taskQueue.pop();
        return t;
    }

public:
    ThreadPool(int threadNum = g_thread_num)
    {
        //  初始化
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_cond, nullptr);

        for (size_t i = 1; i <= threadNum; ++i)
        {
            _threads.push_back(new Thread(i, Routine, this));
        }
    }
    void run()
    {
        for (auto &iter : _threads)
        {
            iter->start();
            // std::cout << iter->name() << " 启动成功" << std::endl;
            LogMessage(NORMAL, "%s: 启动成功 | %s | %d", iter->name().c_str(), __FILE__, __LINE__);
        }
    }
    // void joins() // 测试
    // {
    //     for (auto &iter : _threads)
    //     {
    //         iter->join();
    //     }
    // }
    static void *Routine(void *args) // 消费过程
    {
        ThreadDate *td = (ThreadDate *)args;
        ThreadPool<T> *tp = (ThreadPool<T> *)td->_args; //  类this指针
        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);
        _taskQueue.push(task);       //  私有->公有
        pthread_cond_signal(&_cond); //  唤醒Routine 开始执行任务
    }

    ~ThreadPool()
    {
        for (auto &iter : _threads)
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }

private:
    std::vector<Thread *> _threads;
    std::queue<T> _taskQueue;

    // 线程池本质也是一个生产消费模型
    pthread_mutex_t _lock;
    pthread_cond_t _cond;
};
  • 主函数测试
#include <unistd.h>
#include <ctime>
#include "threadPool.hpp"
#include "Task.hpp"
int main()
{
    srand((u_int64_t)time(nullptr) ^ getpid() ^ 2023210); // 随机数种子
    ThreadPool<Task> *tp = new ThreadPool<Task>();
    tp->run();

    while (true)
    {
        // 生产的过程,制作任务的时候,要花时间
        int x = rand() % 100 + 1;
        usleep(2023);
        int y = rand() % 210 + 1;
        Task t(x, y, [](int x, int y) //  lambda
               { return x * y; });

        // cout << "制作任务完成: " << x << "*" << y << "=?" << endl;
        LogMessage(WARNING, "制作任务完成: %d+%d=?  | %s | %d", x, y, __FILE__, __LINE__);

        // 推送任务到线程池中
        tp->PushTask(t); // 超市-> 私有-》共有
        sleep(1);
    }
    return 0;
}

结果展示

在这里插入图片描述

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

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

相关文章

Linux下文档类型转PDF的总结

我的环境 centos8 先说思路:先把字体上传到服务器,然后更新字体库 ,代码里面配置字体地址。 如果导出的还是乱码,要么没字体,要么检查代码里面的路径。 目录 1.上传windows字体到linux 2.建立索引信息,更新字体缓存

【基于ChatGPT+Python】快速打造前后端分离的OpenAI人工智能聊天机器人

&#x1f680; ChatGPT是最近很热门的AI智能聊天机器人 &#x1f680; 用途方面相比于普通的聊天AI更加的广泛&#xff0c;甚至可以帮助你改BUG&#xff0c;写代码&#xff01;&#xff01;&#xff01; &#x1f680; 下面是使用pythonChatGPTVue实现的在线聊天机器人&#xf…

shell脚本免交互与expect

目录 Here Document 定义 格式 注意 例子 统计行数 修改密码​编辑 expect 定义 基本命令 实验 免交互ssh主机 Here Document 定义 使用I/O重定向的方式将命令列表提供给交互式程序 格式 命令<< 标记....标记 注意 标记可以使用任意的合法字符&#xf…

SpringBoot笔记【JavaEE】

SpringBoot概念、创建和运行 1.什么是SpringBoot&#xff1f;为什么学习SpringBoot&#xff1f; Spring Boot 就是 Spring 框架的脚⼿架&#xff0c;它就是为了快速开发 Spring 框架⽽诞⽣的。 2.Spring Boot优点 快速集成框架【提供启动添加依赖的功能】内容运行容器【无需…

从零开始,打造属于你的 ChatGPT 机器人!

大家好&#xff01;我是韩老师。不得不说&#xff0c;最近 OpenAI/ChatGPT 真的是太火了。前几天&#xff0c;微软宣布推出全新的 Bing 和 Edge&#xff0c;集成了 OpenAI/ChatGPT 相关的技术&#xff0c;带动股价大涨&#xff1a;微软市值一夜飙涨 5450 亿国内外各家大厂也是纷…

为什么神经网络做不了2次函数拟合,网上的都是骗人的吗?

环境&#xff1a;tensorflow2 kaggle 这几天突发奇想&#xff0c;用深度学习训练2次函数。先在网上找找相同的资料这方面资料太少了。大多数如下&#xff1a; 。 给我的感觉就是&#xff0c;用深度学习来做&#xff0c;真的很容易。 网上写出代码分析的比较少。但是也找到了…

云计算|OpenStack|社区版OpenStack安装部署文档(十二--- openstack的网络模型解析---Rocky版)

前言&#xff1a; https://zskjohn.blog.csdn.net/article/details/128846360 云计算|OpenStack|社区版OpenStack安装部署文档&#xff08;六 --- 网络服务neutron的安装部署---Rocky版&#xff09; &#xff08;######注&#xff1a;以上文章使用的是openstack的provider网…

【Vue3】电商网站吸顶功能

头部分类导航-吸顶功能 电商网站的首页内容会比较多&#xff0c;页面比较长&#xff0c;为了能让用户在滚动浏览内容的过程中都能够快速的切换到其它分类。需要分类导航一直可见&#xff0c;所以需要一个吸顶导航的效果。 目标:完成头部组件吸顶效果的实现 交互要求 滚动距离大…

计算机视觉 对比学习13篇经典论文、解读、代码

为了快速对 机器视觉中的对比学习有一个快速了解&#xff0c;或者后续复习&#xff0c;此处收录了 13篇经典论文、一些讲解地较好的博客和相应的Github代码&#xff0c;用不同颜色标记。 ​ 对比学习 13篇经典论文 论文代码和博客http://​www.webhub123.com/#/home/detail?p…

Nextjs了解内容

目录Next.jsnext.js的实现1&#xff0c;nextjs初始化2&#xff0c; 项目结构3&#xff0c; 数据注入getInitialPropsgetServerSidePropsgetStaticProps客户端注入3&#xff0c;CSS Modules4&#xff0c;layout组件5&#xff0c;文件式路由6&#xff0c;BFF层的文件式路由7&…

爬虫笔记之——selenium安装与使用(1)

爬虫笔记之——selenium安装与使用&#xff08;1&#xff09;一、安装环境1、下载Chrome浏览器驱动&#xff08;1&#xff09;查看Chrome版本&#xff08;2&#xff09;下载相匹配的Chrome驱动程序地址&#xff1a;https://chromedriver.storage.googleapis.com/index.html2、学…

vue83-103

vue全局路由拦截路由懒加载路由原理swiper组件选项卡封装电影导航组件正在热映获取数据渲染axios封装详情渲染详情轮播详情Header-组件影院组件渲染全局路由拦截 即使路径对&#xff0c;也会被拦截 router.beforeEach((to,from, next) > { console.log(to) if&#xff08;…

雅思经验(9)

写作&#xff1a;关于趋势的上升和下降在小作文中&#xff0c;真的是非常常见的&#xff0c;所以还是要积累一下。下面给出了很多词&#xff0c;但是在雅思写作中并不是词越丰富&#xff0c;分数就越高的。雅思写作强调的是准确性&#xff1a;在合适的地方用合适的词和句法。不…

【数据库】 数据库中表的基本操作

目录 表的基本操作 一&#xff0c; 创建表 1&#xff0c;单行命令创建表&#xff1a; 2&#xff0c;分行命令创建表&#xff1a; 二&#xff0c; 数据类型 1&#xff0c;文本类型&#xff1a; 2&#xff0c;数值类型&#xff1a; 3&#xff0c;日期/时间类型&#xff1a…

软件测试金融测试岗位,本人亲面

网上银行转账是怎么测的&#xff0c;设计一下测试用例。 回答思路&#xff1a; 宏观上可以从质量模型&#xff08;万能公式&#xff09;来考虑&#xff0c;重点需要测试转账的功能、性能与安全性。设计测试用例可以使用场景法为主&#xff0c;先列出转账的基本流和备选流。然…

一个图片对比的小工具【小工具制作】

目录逐一击破确定架构图片上传自适应生成对比界面切换对比模式打分功能审核进行提交项目负责人查看问题并改正总结前言&#xff1a;这是一个实际的需求&#xff0c;因为需要设计师给的原图和同学们制作出来的项目成品图进行比对打分&#xff0c;所以就有了一个图片对比的小工具…

用 Python 调用 GPT-3 API

用 Python 调用 GPT-3 API GPT-3 是去年由 Open AI 推出的语言机器学习模型。它因其能够写作、写歌、写诗&#xff0c;甚至写代码而获得了广泛的媒体关注&#xff01;该工具免费使用&#xff0c;只需要注册一个电子邮件即可。 GPT-3 是一种叫 transformer 的机器学习模型。具体…

C语言(输入printf()函数)

printf()的细节操作很多&#xff0c;对于现阶段的朋友来说&#xff0c;主要还是以理解为主。因为很多的确很难用到。 目录 一.转换说明&#xff08;占位符&#xff09; 二.printf()转换说明修饰符 1.数字 2.%数字1.数字2 3.整型转换字符补充 4.标记 -符号 符号 空格符…

JavaWEB必知必会-Servlet

目录 Servlet简介Servlet快速入门Servlet配置详解ServletContext 1 Servlet简介 Servlet 运行在服务端的Java小程序&#xff0c;是sun公司提供一套规范&#xff08;接口&#xff09;&#xff0c;用来处理客户端请求、响应给浏览器的动态资源。但servlet的实质就是java代码&a…

电脑里的连接速度双工模式是什么?怎么设置

双工模式包括全双工、半双工模式。1.半双工1、半双工数据传输允许数据在两个方向上传输&#xff0c;但是&#xff0c;在某一时刻&#xff0c;只允许数据在一个方向上传输&#xff0c;它实际上是一种切换方向的单工通信。所谓半双工就是指一个时间段内只有一个动作发生。早期的对…