多线程模板应用实现(实践学习笔记)

news2024/9/22 7:21:37

出处:B站码出名企路

个人笔记:因为是跟着b站的教学视频以及文档初步学习,可能存在诸多的理解有误,对大家仅供借鉴,参考,然后是B站up阳哥的视频,我是跟着他学。大家有兴趣的可以到b站搜索。加油,一起学习。我的问题,大家如果看见,希望可以提出指正,谢谢大家。

应用场景

多线程的应用场景非常多,常见的有:

  1. 网络通信:在网络通信应用中,一般需要同时处理多个请求,如果使用单线程模式,会阻塞其他请求,造成性 能瓶颈,因此使用多线程可以提高并发处理能力。

  2. 数据库操作:在数据库操作中,有时需要同时对多个数据表进行操作,使用多线程可以提高处理效率。

  3. 图像处理:在图像处理应用中,需要对多个图像进行处理,在单线程模式下,处理速度会很慢,使用多线程可 以提高处理速度。

  4. 游戏开发:在游戏开发中,常常需要同时处理多个任务,比如处理游戏画面、物理效果、声音效果等,使用多 线程可以提高游戏的运行速度和流畅度。

  5. 并行计算:在科学计算领域中,常常需要对大量数据进行处理和计算,使用多线程可以将计算任务划分到多个 线程中进行,从而提高计算速度。

总之,多线程在提高程序性能、响应性和资源利用率方面有着广泛的应用。然而,需要注意在多线程编程中处理线程同步、共享数据等问题,以确保程序的正确性和稳定性。

图解结构

模块拆解

第一步:StateSubmitor耗时内容处理类

此处并没有很多具体实现,因为要结合业务。比如耗时处理逻辑

  class StateSubmitor    
    {
        public:

            explicit StateSubmitor(const std::string& str);
            ~StateSubmitor();
            
            //submit: 提交到队列中
            //const std::string& content 内容,包括海量数据
            void submit(const std::string& content);//content可任意
            //flush: 将队列中的所有状态信息发往远程收集端
            //具体的业务逻辑
            void flush();

        private:

            StateSubmitor(const StateSubmitor&) = delete;
            StateSubmitor& operator=(const StateSubmitor&) = delete;
    };
    void StateSubmitor::submit(const std::string& content){
        /*
            @ 对 content的耗时处理逻辑
        
        */
    }
第二步:NodeMonitor线程启动类
//节点监控, 监控任务的发生, 业务的产生. 多线程同步等控制逻辑的封装
    class NodeMonitor
    {
        public:
            ~NodeMonitor();
            static NodeMonitor* instance();

            void start();
            void shutdown();
            bool init();

        private:
            NodeMonitor();

            NodeMonitor(const NodeMonitor&) = delete;
            NodeMonitor& operator=(const NodeMonitor&) = delete;
            void stateInfo(const std::string& strs);

            void ThreadFunc();                         //消费者线程入口函数

            bool shutdown_;                            //开关   
            std::mutex mutex_;                         
            std::thread thread_;                       //消费者线程
            std::condition_variable cond_;
            //queue
            std::queue<std::string> task_queue_;       //任务队列
            std::unique_ptr<StateSubmitor> submitor_;  //unique_ptr管理submitor对象
    };

}

具体实现,这里才是多线程同步互斥的重点部分,核心,利用任务队列做缓冲容器,解耦合。使得生产者线程和消费者线程之间的耦合度降低,生产者只管将任务放入任务队列,然后即可返回,无需等待消费者处理。消费者只管从任务队列中拿取任务处理。大大提高效率。通过缓存大大减低了生产者和消费者之间的耦合程度。

生活场景:快递驿站,快递小哥就是生产者,我们就是消费者。快递驿站就是容器队列。

 //析构一般独立一个函数
    NodeMonitor::~NodeMonitor(){
        this->shutdown();//做资源释放等等操作
    }
    //创建线程安全的单例
    //call_once 确保多线程下仅仅创建一个NodeMonitor对象
    NodeMonitor* NodeMonitor::instance(){
        static NodeMonitor* instance = nullptr;
        static std::once_flag flag;  
        std::call_once(flag, [&]{
            instance = new (std::nothrow) NodeMonitor();
        });

        return instance; 
    }
    
    //线程启动
    void NodeMonitor::start(){
        //创建消费者
        thread_ = std::thread(&NodeMonitor::ThreadFunc, this);
        //启动生产者
        if (!init()){
            return;
        }
    }
    //生产者函数
    bool NodeMonitor::init(){
        submitor_.reset(new StateSubmitor("lyy")); //创建submitor
        /*
          @ 不断地填充stateInfo
          @ 如果是实际应用场景可能会采取轮询, 或者是event事件触发, 
          此处阳哥按照最简单的塞入文本信息作为事件(任务)
        */
        while (true)
        {
            stateInfo("lxk");
        }
        return true;
    }
    
    //填入需要的信息 <=> push任务
    void NodeMonitor::stateInfo(const std::string& strs){
        std::unique_lock<std::mutex> lock(mutex_);
        task_queue_.push(strs); //生产, 塞入任务
        cond_.notify_one();     //通知消费
    }
    
    //线程销毁
    void NodeMonitor::shutdown(){
        std::unique_lock<std::mutex> lock(mutex_);
        shutdown_ = true;
        cond_.notify_all();
        if (thread_.joinable()){
            thread_.join();
        }
    }
    
    //消费者函数
    void NodeMonitor::ThreadFunc(){

        while (!shutdown_)
        {
            std::unique_lock<std::mutex> lock(mutex_);
            cond_.wait(lock, [this]{
                return shutdown_ || !task_queue_.empty();
            });

            if (shutdown_){
                break;
            }

            std::string str = task_queue_.front();
            task_queue_.pop();
            lock.unlock();

            submitor_->submit(str);//提交状态信息
        }
        
    }

具体案例

消息队列作业实现

#include <iostream>
#include <queue>
#include <mutex>
#include <thread>
#include <memory>
#include <condition_variable>
#include <string>
#include <chrono>

namespace XX
{
    class MessageQueue {//封装消息队列类
    public:
        void push(const std::string& message); 
        std::string pop(); 
        bool empty();
    private:
        std::mutex mutex_; //互斥锁, 保障互斥操作
        std::condition_variable cond_; //通知, 保障同步
        std::queue<std::string> msg_queue_;  //容器
    };
        
    class StateSubmitor {//消息处理类, 业务处理, 管理消息队列
    public:
        explicit StateSubmitor(MessageQueue& msg_queue);
        ~StateSubmitor();
        void submit(const std::string& content); //提交状态信息并将其添加到队列中
        void flush();  //flush: 将队列中的所有状态信息发往远程收集端, 清空处理所有消息.
    private:
        StateSubmitor(const StateSubmitor &) = delete;
        StateSubmitor &operator=(const StateSubmitor &) = delete;
    private:
        MessageQueue& msg_queue_;  //消息队列
    };

    // 节点监控, 监控任务的发生, 业务的产生. 多线程同步等控制逻辑的封装
    class NodeMonitor {
    public:
        ~NodeMonitor();
        static NodeMonitor *instance();

        void start();
        void shutdown();
        bool init();

    private:
        NodeMonitor();
        void ProducerThreadFunc(); //线程函数
        void ConsumerThreadFunc(); //线程函数
        NodeMonitor(const NodeMonitor &) = delete;
        NodeMonitor &operator=(const NodeMonitor &) = delete;
    private:
        std::thread producer_thread_; //生产者线程,不停的往消息队列塞入监控到的用户状态信息消息.
        static int count_;
        std::unique_ptr<StateSubmitor> submitor_;
        MessageQueue msg_queue_; //消息队列
        std::thread consumer_thread_;//消费者线程, 不停的从消息队列中抽出消息进行处理
        bool shutdown_;              //开关
    };
}

namespace XX {

    int NodeMonitor::count_ = 0;//初始化
    void MessageQueue::push(const std::string& message) {
        std::unique_lock<std::mutex> lock(mutex_);
        msg_queue_.push(message);//塞入消息
        cond_.notify_one();//通知消费
    }

    std::string MessageQueue::pop() {
        std::unique_lock<std::mutex> lock(mutex_);
        cond_.wait(lock, [this]{//等待消息到来
            return !empty();
        });
        std::string msg = msg_queue_.front();//拿到消息
        msg_queue_.pop();
        return msg;
    }

    bool MessageQueue::empty() {
        return msg_queue_.empty();
    }
    
    StateSubmitor::StateSubmitor(MessageQueue& msg_queue)
        : msg_queue_(msg_queue) {}  

    void StateSubmitor::submit(const std::string& content) {
        //提交状态信息消息的业务操作
        std::cout << "消息为: " << content << std::endl;
        //将业务状态消息push到消息队列中
        msg_queue_.push(content);
    }

    void StateSubmitor::flush() {
        //清空所有消息
    }

    StateSubmitor::~StateSubmitor() {
        this->flush();
    }
    
    NodeMonitor::NodeMonitor():shutdown_(false){

    }

    NodeMonitor::~NodeMonitor(){
        this->shutdown();//释放资源...操作
    }

    void NodeMonitor::ProducerThreadFunc() {
        while (!shutdown_) { //不断生产
            std::this_thread::sleep_for(std::chrono::milliseconds(3000));
            std::string msg = "消息";
            msg += std::to_string(count_);
            count_ ++;
            submitor_->submit(msg);
        }
    }

    NodeMonitor* NodeMonitor::instance(){
        static NodeMonitor* instance = nullptr;
        static std::once_flag flag;  
        std::call_once(flag, [&]{
            instance = new (std::nothrow) NodeMonitor();
        });

        return instance; 
    }

    void NodeMonitor::ConsumerThreadFunc() {
        while (!shutdown_) { //不断消费
            std::this_thread::sleep_for(std::chrono::milliseconds(2000));
            std::string msg = msg_queue_.pop();//弹出一条消息
            std::cout << "处理了: " << msg << std::endl;
        }
    }

    void NodeMonitor::start() {
        init();
    }

    void NodeMonitor::shutdown() {
        shutdown_ = true;
    }

    bool NodeMonitor::init() {
        submitor_.reset(new StateSubmitor(msg_queue_)); //创建submitor
        //创建生产者,消费者线程并且join
        producer_thread_ = std::thread(&NodeMonitor::ProducerThreadFunc, this);
        consumer_thread_ = std::thread(&NodeMonitor::ConsumerThreadFunc, this);

        producer_thread_.join();
        consumer_thread_.join();
        return true;
    }
}

int main() {

    XX::NodeMonitor::instance()->start();
    return 0;
}

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

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

相关文章

C/C++ 联合体

目录 联合体概述 联合体的内存分配 联合体大小计算 联合体概述 联合与结构非常的相似&#xff0c;主要区别就在于联合这两个字。 联合的特征&#xff1a;联合体所包含的成员变量使用的是同一块空间。 联合体定义 //联合类型的声明 union Un {char c;int i; }; //联合变量…

isaacgym(legged_gym)学习 (二)—— 设置环境地形

isaacgym(legged_gym)学习 &#xff08;二&#xff09;—— 设置环境地形 文章目录 isaacgym(legged_gym)学习 &#xff08;二&#xff09;—— 设置环境地形前言一、了解isaacgym中地形如何构成的二、自定义修改1.代码2.查看script/play.py 总结 前言 如何设置isaacgym中的环…

【操作系统篇】正在持续写入的日志如何清理?

正在持续写入的日志如何清理? ✔️典型解析✔️ 推荐的日志管理工具✔️如何设置日志轮转工具✔️如何选择适合我的Logrote-NG日志轮转工具✔️如何测试Logrote-NG工具的稳定性✔️分析一个简单的ELK报警✔️这个报警的作用是什么✔️报警机制的优缺点✔️如何减少报警机制的误…

蓝牙运动耳机哪款好用?运动用什么耳机比较好?2024运动耳机推荐

​在众多的耳机类型中&#xff0c;运动耳机因其独特的设计和功能而备受青睐。它们不仅要具备出色的音质&#xff0c;还需要能够适应激烈的运动环境&#xff0c;如防水、防汗、牢固耐用等。今天&#xff0c;我想向大家推荐一些在这些方面表现出色的运动耳机&#xff0c;这些耳机…

Unity 圆角 线段 绘制 LineRender

需求 绘制圆角 核心函数 /// <summary>/// 点ABC 形成的角度必须为90 点c为中间的点/// </summary>/// <param name"a"></param>/// <param name"b"></param>/// <param name"c"></param>/// &…

C++学习笔记——友元及重载运算符

目录 一、友元 1.1声明友元函数 1.2声明友元类 二、运算符重载 2.1重载加号运算符 2.2重载流插入运算符 三、一个简单的银行管理系统 四、 详细的介绍 一、友元 在 C 中&#xff0c;友元是一个函数或类&#xff0c;它可以访问另一个类的私有成员或保护成员。通常情况下…

微服务实战系列之Filter

前言 Filter&#xff0c;又名过滤器&#xff0c;当然不是我们日常中见到的&#xff0c;诸如此类构件&#xff1a; 而应该是微服务中常使用的&#xff0c;诸如此类&#xff08;图片来自官网&#xff0c;点击可查看原图&#xff09;&#xff1a; 一般用于字符编码转换&#xf…

Unity DOTS中的baking(二)Baker的触发

Unity DOTS中的baking&#xff08;二&#xff09;Baker的触发 我们知道&#xff0c;当传入Baker的authoring component的值发生变化时&#xff0c;就会触发baking。不过在有些情况下&#xff0c;component所引用的对象没有变化&#xff0c;而是对象自身内部的一些属性发生了变化…

DNS--windows sever 2012 r2

1 安装dns服务 得到 2 配置正向解析文件 设置正向解析文件的别名 配置(1)主机名 (2)别名&#xff08;3)邮件交换器 最终得到 3 配置反向文件 建立指针 得到 4 验证

【百科物理】-1.弹力和压力

导入 问题&#xff1a; 为什么在蹦床上弹得更高&#xff1f; 现象背后的原理&#xff0c;因为有力。 力&#xff1a;物体(物质)与物体(物质)之间推、拉、挤压的相互作用。力可以改变物体的运动状态&#xff08;比如踢足球&#xff09;&#xff0c;力可以改变物体的形状&#xf…

ProtoBuf一些踩坑记录

一、Protobuf学习基础 学习的资料很多也很全&#xff0c;这里添加几个链接进行Protobuf的基础学习的链接&#xff0c;链接中的案例使用C编辑&#xff1a; 链接&#xff1a;Protobuf介绍及简单使用(上&#xff09;_google_protobuf_version-CSDN博客 Protobuf介绍及简单使用(下&…

行云部署成长之路 -- 慢 SQL 优化之旅 | 京东云技术团队

当项目的SQL查询慢得像蜗牛爬行时&#xff0c;用户的耐心也在一点点被消耗&#xff0c;作为研发&#xff0c;我们可不想看到这样的事。这篇文章将结合行云部署项目的实践经验&#xff0c;带你走进SQL优化的奇妙世界&#xff0c;一起探索如何让那些龟速的查询飞起来&#xff01;…

three.js 学习笔记(学习中1.7更新) |

文章目录 three.js 学习笔记入门基础概念透视相机 第一个three.js应用threejs画布尺寸和布局canvas画布宽高度动态变化 坐标辅助器 THREE.AxesHelper实现动画效果requestAnimationFrame时间相关属性和方法 THREE.Clock类 相机控件 轨道控制器OrbitControls 灯光点光源点光源辅助…

【ArcGIS微课1000例】0087:经纬度格式转换(度分秒转度、度转度分秒)

ArcGIS软件可以很方便的直接实现度分秒转度、度转度分秒。 文章目录 一、转换预览二、工具介绍三、案例解析一、转换预览 借助ArcGIS快速实现度分秒与度及其他格式的坐标转换。例如:度分秒→度 度分秒: 度: 二、工具介绍 转换坐标记法:将一个或两个字段包含的坐标记法从一…

PAT 乙级 1049 数列的片段和

分数 20 作者 CAO, Peng 单位 Google 给定一个正数数列&#xff0c;我们可以从中截取任意的连续的几个数&#xff0c;称为片段。例如&#xff0c;给定数列 { 0.1, 0.2, 0.3, 0.4 }&#xff0c;我们有 (0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3) …

【JAVA】Iterator 和 ListIterator 有什么区别?

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 在Java中&#xff0c;遍历集合是日常编程中常见的任务&#xff0c;而Iterator和ListIterator作为遍历集合的两个主要接口&#xff0…

【MySQL】索引基础

文章目录 1. 索引介绍2. 创建索引 create index…on…2.1 explain2.2 创建索引create index … on…2.3 删除索引 drop index … on 表名 3. 查看索引 show indexes in …4. 前缀索引4.1 确定最佳前缀长度&#xff1a;索引的选择性 5. 全文索引5.1 创建全文索引 create fulltex…

Angular学习第二天--问题记录

一、问题 1.用脚手架搭建完项目之后&#xff0c;缺少app.modules.ts文件&#xff0c; 2.解决办法&#xff1a; 在终端继续输入命令 ng new 项目名称 --no-standalone --routing --ssrfalse 3.完整目录&#xff1a; 二、问题 1.问题来源&#xff0c;源代码&#xff1a; <fo…

K8S API访问控制之RBAC利用

前言 K8S对于API的访问安全提供了访问控制&#xff0c;主要为4个阶段&#xff0c;本文为第二个阶段——鉴权的RBAC。RBAC是基于角色的访问控制&#xff0c;使用kubeadm安装集群1.6版本以上的都默认开启了RBAC。本文主要研究集群可能存在的利用点及相对应的利用手法。 API访问…

kettle的基本介绍和使用

1、 kettle概述 1.1 什么是kettle Kettle是一款开源的ETL工具&#xff0c;纯java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c;绿色无需安装&#xff0c;数据抽取高效稳定。 1.2 Kettle核心知识点 1.2.1 Kettle工程存储方式 以XML形式存储以资源库方式存储…