重写muduo之Thread、EventLoopThread、EventLoopThreadPool

news2025/4/5 9:20:52

目录

1、概述

2、Thread

2.1 Thread.h

3、EventLoopThread

3.1 EventLoopThread.h

3.2  EventLoopThread.cc

4、 EventLoopThreadPool

4.1 EventLoopThreadPool.h

4.2 EventLoopThreadPool.cc


1、概述

管理事件循环线程的调度的

打包了一个EventLoop和线程,绑定了一个loop跟thread,让这个loop运行在这个thread上,在这个thread里面创建一个loop(one loop per thread)

底层封装的线程

2、Thread

2.1 Thread.h

#pragma once

#include "noncopyable.h"

#include <functional>
#include <thread>
#include <memory>
#include <string>
#include <atomic>

class Thread:noncopyable
{
public:
    using ThreadFunc=std::function<void()>;//线程函数的函数类型  绑定器和函数对象,就可以传参 

    explicit Thread(ThreadFunc,const std::string& name=std::string());//构造函数
    ~Thread();

    void start();//启动当前线程 
    void join();//当前线程等待其他线程完了再运行下去 

    bool started()const {return started_;}
    pid_t tid()const{return tid_;}
    const std::string& name()const{return name_;}

    static int numCreated(){return numCreated_;}
private:
    void setDefaultName();//给线程设置默认的名称
    bool started_;//启动当前线程 
    bool joined_;//当前线程等待其他线程完了再运行下去 
    std::shared_ptr<std::thread> thread_;//自己来掌控线程对象产生的时机
    pid_t tid_;
    ThreadFunc func_;//存储线程函数 
    std::string name_;//调试的时候打印 
    static std::atomic_int numCreated_;//对线程数量计数 
};

 我们使用C++结合lambda表达式 的方法来实现,非常方便。

3、EventLoopThread

3.1 EventLoopThread.h

#pragma once

#include "noncopyable.h"
#include "Thread.h"

#include <functional>
#include <mutex>
#include <condition_variable>
#include <string>

class EventLoop;

class EventLoopThread:noncopyable
{
public:
    using ThreadInitCallback=std::function<void(EventLoop*)>;

    EventLoopThread(const ThreadInitCallback& cb=ThreadInitCallback(), //线程初始化的回调 
        const std::string& name=std::string());
    ~EventLoopThread();

    EventLoop* startLoop();//开启循环 
private:
    void threadFunc();//线程函数,创建loop 

    EventLoop* loop_;
    bool exiting_;//是否退出循环
    Thread thread_;
    std::mutex mutex_;
    std::condition_variable cond_;
    ThreadInitCallback callback_;//初始化操作 
};

3.2  EventLoopThread.cc

#include "EventLoopThread.h"
#include "EventLoop.h"


EventLoopThread::EventLoopThread(const ThreadInitCallback& cb, //线程初始化的回调 
        const std::string& name)
        :loop_(nullptr)
        ,exiting_(false)
        ,thread_(std::bind(&EventLoopThread::threadFunc,this),name)//绑定回调函数
        ,mutex_()
        ,cond_()
        ,callback_(cb)
        {

        }
EventLoopThread::~EventLoopThread()
{
    exiting_=true;
    if(loop_!=nullptr)
    {
        loop_->quit();
        thread_.join();
    }
}

EventLoop* EventLoopThread::startLoop()//开启循环 
{
    thread_.start();//启动底层的新线程
	//启动后执行的是EventLoopThread::threadFunc

    EventLoop* loop=nullptr;
    {
        std::unique_lock<std::mutex> lock(mutex_);
        while(loop_==nullptr)//loop指针还没有初始化
        {
            cond_.wait(lock);挂起,等待
        }
        loop=loop_;
    }
    return loop;
}

//下面这个方法,是在单独的新线程里面运行的
void EventLoopThread::threadFunc()//线程函数,创建loop 
{
    EventLoop loop;//创建一个独立的eventloop,和上面的线程是一一对应的,one loop per thread

    if(callback_)//如果有回调
    {
        callback_(&loop);//绑定loop做一些事情
    }

    {
        std::unique_lock<std::mutex> lock(mutex_);
        loop_=&loop;//就是运行在这个线程的loop对象,将这个对象初始化好之后(loop指针指向loop对象),才能唤醒(通知)
        cond_.notify_one();//唤醒1个线程,被唤醒后去访问loop指针
    }
    loop.loop();//EventLoop loop=>Poller.poll
    std::unique_lock<std::mutex> lock(mutex_);
    loop_=nullptr;
}

4、 EventLoopThreadPool

这个很明显,是池的概念。是一个事件线程池,管理eventloop,eventloop绑定的就是一个线程。

用户最开始创建的loop,对应的是一个线程

4.1 EventLoopThreadPool.h

#pragma once
#include "noncopyable.h"

#include <functional>
#include <string>
#include <vector>
#include <memory>

class EventLoop;
class EventLoopThread;

class EventLoopThreadPool:noncopyable
{
public:
    using ThreadInitCallback=std::function<void(EventLoop*)>;

    EventLoopThreadPool(EventLoop* baseLoop,const std::string& nameArg);
    ~EventLoopThreadPool();

    void setThreadNum(int numThreads){numThreads_=numThreads;}

    void start(const ThreadInitCallback& cb=ThreadInitCallback());

    //如果工作在多线程中,baseloop_默认以轮询的方式分配channel给subloop
    EventLoop* getNextLoop();

    std::vector<EventLoop*> getAllLoops();

    bool started()const{return started_;}
    const std::string name() const{return name_;}
private:
    EventLoop* baseLoop_;//EventLoop loop;
    std::string name_;
    bool started_;
    int numThreads_;
    int next_;
    std::vector<std::unique_ptr<EventLoopThread>> threads_;
    std::vector<EventLoop*> loops_;
};

4.2 EventLoopThreadPool.cc

#include "EventLoopThreadPool.h"
#include "EventLoopThread.h"

#include <memory>

 EventLoopThreadPool::EventLoopThreadPool(EventLoop* baseLoop,const std::string& nameArg)
    :baseLoop_(baseLoop)
    ,name_(nameArg)
    ,started_(false)
    ,numThreads_(0)
    ,next_(0)
 {}
EventLoopThreadPool::~EventLoopThreadPool()
{}

void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
    started_=true;

    for(int i=0;i<numThreads_;i++)
    {
        char buf[name_.size()+32];
        snprintf(buf,sizeof buf,"%s%d",name_.c_str(),i);
        EventLoopThread* t=new EventLoopThread(cb,buf);
        threads_.push_back(std::unique_ptr<EventLoopThread>(t));
        loops_.push_back(t->startLoop());//底层创建线程,绑定一个新的EventLoop,并返回该loop的地址
    }

    //整个服务端只有一个线程,运行着baseloop
    if(numThreads_==0&&cb)
    {
        cb(baseLoop_);
    }
}

//如果工作在多线程中,baseloop_默认以轮询的方式分配channel给subloop
//通过轮询的方式从子线程中取loop(循环)
//IO线程  baseloop  用作处理用户的连接事件
//工作线程  新创建的loop  用于处理用户的读写事件
EventLoop* EventLoopThreadPool::getNextLoop()
{
    EventLoop* loop=baseLoop_;

    if(!loops_.empty())//通过轮询获取下一个处理事件的loop
    {
        loop=loops_[next_];
        next_++;
        if(next_>=loops_.size())
        {
            next_=0;
        }
    }
    return loop;
}

std::vector<EventLoop*> EventLoopThreadPool::getAllLoops()
{
    if(loops_.empty())
    {
        return std::vector<EventLoop*>(1,baseLoop_);
    }
    else
    {
        loops_;
    }
}

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

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

相关文章

每日OJ题_记忆化搜索①_力扣509. 斐波那契数(四种解法)

目录 记忆化搜索概念和使用场景 力扣509. 斐波那契数 解析代码1_循环 解析代码2_暴搜递归 解析代码3_记忆化搜索 解析代码4_动态规划 记忆化搜索概念和使用场景 记忆化搜索是一种典型的空间换时间的思想&#xff0c;可以看成带备忘录的爆搜递归。 搜索的低效在于没有能够…

Mysql基础(五)外键约束

一 外键 激励&#xff1a; 每天进步一点点即可 ① 思考 1、在MySQL中,我们知道主键 PRIMARY KEY的主要作用是唯一区分表中的各个行 [记录];思考&#xff1a;但是对于外键 foreign key比较陌生? 那么外键作用以及限制条件和目的呢? ② 外键的定义 1、外键是某个表 A中…

DRF视图基类使用方法

【 一 】drf之请求 请求对象Request 【 0 】前言 ​ 在 Python 中&#xff0c;通常通过 request 对象来处理 HTTP 请求&#xff0c;尤其是在 web 开发中&#xff0c;比如使用 Django、Flask 等框架时会经常接触到这个对象。request 对象是框架提供的&#xff0c;用于封装客户…

YOLOv5改进(二)BiFPN替换Neck网络

前言 针对红绿灯轻量化检测&#xff0c;上一节使用MobileNetv3替换了主干网络&#xff0c;本篇将在使用BiFPN替换Neck的方式优化算法~ 往期回顾 YOLOv5改进&#xff08;一&#xff09;MobileNetv3替换主干网络 目录 一、BiFPN简介二、改进方法一第一步&#xff1a;在common.…

实战28套JAVA高端架构P6/P7/P8架构—全栈架构

概述 Java SE Java SE&#xff08;Java Platform&#xff0c;Standard Edition&#xff09;。Java SE 以前称为J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的Java应用程序。Java SE 包含了支持Java Web 服务开发的类&#xff0c;并为Java Platform&…

《从零开始,搭建一个简单的UVM验证平台》实操

最近的工作中需要用UVM平台去仿真软件同事写的C程序&#xff0c;虽然只要用EDA同事已经搭好的UVM平台稍微改改就行&#xff0c;但对于我这种从未接触过UVM甚至都没用过System Verilog的纯FPGA工程师来说还是很有难度的&#xff0c;因为我对这方面一点概念都没有。 基于此&…

批量网络装机

1. PXE概念 PXE(preboot execute environment&#xff0c;预启动执行环境)是由Intel公司开发的最新技术&#xff0c;工作于Client/Server的网络模式&#xff0c;支持工作站通过网络从远端服务器下载映像&#xff0c;并由此支持通过网络启动操作系统。在启动过程中&#xff0c;…

【智能算法】雪消融优化算法(SAO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2023年&#xff0c;L Deng受到雪升华和融化行为启发&#xff0c;提出了雪消融优化算法&#xff08;Snow Ablation Optimizer, SAO&#xff09;。 2.算法原理 2.1算法思想 SAO模拟了雪的…

微信云小程序快速上手云数据库+云函数+云存储的操作

&#x1f680; 作者 &#xff1a;“二当家-小D” &#x1f680; 博主简介&#xff1a;⭐前荔枝FM架构师、阿里资深工程师||曾任职于阿里巴巴担任多个项目负责人&#xff0c;8年开发架构经验&#xff0c;精通java,擅长分布式高并发架构,自动化压力测试&#xff0c;微服务容器化k…

JSP技术讲解

目录 1、JSP简介 2、JSP体验 3、JSP运行原理 4、JSP基本语法 5、JSP指令 6、JSP内置九大对象 7、JSP标签 8、JSP配置 9、JSP排错 10、总结 在前面的Servlet学习中发现Servlet本质是一个java程序&#xff0c;因此Servlet更加擅长编写程序的业务逻辑&#xff0c;而如果要…

Java特性之设计模式【代理模式】

一、代理模式 概述 在代理模式&#xff08;Proxy Pattern&#xff09;中&#xff0c;一个类代表另一个类的功能。这种类型的设计模式属于结构型模式 在代理模式中&#xff0c;我们创建具有现有对象的对象&#xff0c;以便向外界提供功能接口 主要解决&#xff1a; 在直接访问…

Apple强大功能:在新款 iPad Pro 和 iPad Air 中释放 M4 芯片潜力

Apple 的最新强大功能&#xff1a;在新款 iPad Pro 和 iPad Air 中释放 M4 芯片的潜力 概述 Apple 推出配备强大 M4 芯片的最新 iPad Pro 和 iPad Air 型号&#xff0c;再次突破创新界限。新一代 iPad 有望彻底改变我们的工作、创造和娱乐方式。凭借无与伦比的处理能力、令人惊…

CTF-reverse,逆向分析,对“左移4或右移4,即(x<<4) | (x >>4)的加密探讨

博主在刷题过程中遇上这样一个有意思的加密&#xff08;如下图&#xff09;&#xff0c;苦苦思索其逆向运算&#xff0c;被硬控了很久&#xff0c;也没搜到什么资料来解释这个问题&#xff08;也许是太简单&#xff1f;&#xff1f;蒟蒻博主怀疑人生……&#xff09; 经过博主不…

【MATLAB源码-第205期】基于matlab的LDPC译码算法仿真,对比BF算法,最小和算法,对数BP和概率BP四种算法。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 LDPC 码简介 LDPC码是一种通过稀疏奇偶校验矩阵定义的线性分组码&#xff0c;1962年由Gallager首次提出。这种码具有高效的解码性能&#xff0c;尤其在接近香农极限的情况下&#xff0c;其性能表现尤为突出。LDPC码的核心特…

Logstash分析MySQL慢查询日志实践

删除匹配到的行&#xff0c;当前行信息不记录到message中

VMware Workstation 虚拟机学习 安装centos7.9

1打开VMware Workstation--文件--新建虚拟机 #选择对应的&#xff0c;本例选择最新的 2开始安装centos #网卡配置 vi /etc/sysconfig/network-scripts/ifcfg-ens33 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic DEFROUTEyes IPV4_FAILURE_FATALno IPV6INITy…

Linux:进程通信(二)信号的保存

目录 一、信号的处理是否是立即处理的&#xff1f; 二、信号如何保存 1、阻塞、未决、递达 2、信号集 3、信号集操作函数 4、sigprocmask函数 5、sigpending 函数 上篇文章我们讲解了信号的产生&#xff1a;Linux&#xff1a;进程信号&#xff08;一&#xff09;信号的产…

【C语言】字符函数和字符串函数--超详解

前言&#xff1a; 在编程的过程中&#xff0c;我们经常要处理字符和字符串&#xff0c;为了⽅便操作字符和字符串&#xff0c;C语⾔标准库中提供了 ⼀系列库函数&#xff0c;接下来我们就学习⼀下这些函数。 1. 字符分类函数 C语⾔中有⼀系列的函数是专⻔做字符分类的&#…

实战BACnet/IP标准通信网关在楼宇自动化中的应用

智慧楼宇建设实现不同设备间的互联互通是一项巨大挑战&#xff0c;尤其是在那些历史悠久的建筑中&#xff0c;新旧系统并存的情况尤为普遍。某大型商业综合体就面临着这样的困境&#xff1a;老旧的暖通空调系统采用Modbus RTU协议&#xff0c;而新部署的能源管理系统却要求BACn…

一文搞懂MySQL索引的数据结构

一、引言 在数据库管理系统中&#xff0c;索引是提高查询性能的关键所在。对于MySQL这类关系型数据库来说&#xff0c;索引更是其优化查询不可或缺的一部分。索引能够大大加快数据的检索速度&#xff0c;减少数据库的I/O操作&#xff0c;提高数据库的整体性能。本文将从索引的…