当前位置: 首页 > news >正文

并发编程(c++)——5.事件驱动

在并发编程中除了池和流的方式外,还存在一种基本形式,就是事件驱动。
事件驱动的思想是使用一个线程,不断循环处理任务,将任务分发给其他复用的线程,这样就通过单线程处理大量任务。

发展历程

io密集任务可以使用多线程,一个io使用一个线程。
但是如果任务过多,比如达到百万级,此时线程数量也会达到百万级别,此时系统支持不了,会崩溃。
或者说使用线程池,但是线程数量也需要很大,频繁的切换线程也会带来大量的开销。

这里有一个前提是,io任务的处理机制基本一致,可以用一个函数处理大量任务。
此时可以考虑一种方式,使用少量线程,完成大量io任务。
使用一个死循环,一个队列,队列中存储io任务,死循环中不断处理io,这样就可以通过单线程处理百万io。

但是这样会有阻塞问题,处理io会使cpu等待,所以需要将请求io和处理io分离。
这样cpu只处理请求io,处理io交给其他线程处理,能充分利用cpu资源。

同时还有一个问题,循环内部会查询io任务队里,但如果io队列里没有任务,这个循环就没有意义,此时cpu会空转,浪费资源。
所以需要一种机制,当io队列为空时,让cpu等待,当有任务时再唤醒cpu,继续处理任务。

背景问题

1.使用多线程处理io密集任务,当线程数量过大,带来系统支持线程数不足,线程切换资源消耗大。
2.cpu和io处理时间差大,带来的阻塞问题。
3.非阻塞循环,带来cpu空转。

解决方案

1.循环
使用一个死循环,不断读取任务和将任务分配给处理器。
解决多线程处理,使用单线程实现。
2.分离
发起io请求后,不等待io返回,继续处理其他任务,当io返回后,再处理io返回结果。
解决阻塞问题。
3.监督
循环内部监视任务,当任务为空时,让cpu等待,当有任务时再唤醒cpu,继续处理任务。
解决cpu空转问题。

实现

将所有任务抽象为不同的数据和对应的处理方式,这就带获得了各个组成部分。

实现因素

1.事件
将类型和数据抽象为一个结构体,这个结构体就是事件。
2.任务队列
将要处理的事件放入队列中,循环从队列中获取事件,处理事件。
3.处理器队列
将任务类型和处理方式抽象为一个map,key为任务类型,value为处理方式,放置在一个队列中,处理任务时搜索这个队列,找到对应任务类型,执行处理方式。
4.注册
将处理器注册到处理器队列中。
5.循环
死循环,调度任务队列和处理器队列,监督任务队列,当任务队列为空时,让cpu等待,当有任务时再唤醒cpu,继续处理任务。
6.分发
有任务时查找任务对应的处理器,将数据输入处理器中,处理数据。

#include<string>#include<queue>#include<map>#include<vector>#include<functional>#include<mutex>structevent{inttype;std::string data;};// 事件驱动类,用于处理事件驱动的程序classEventDrive{private:// 事件队列,用于存储待处理的事件std::queue<event>queue_events;// 事件处理器映射表,键为事件类型,值为对应的事件处理函数列表std::map<int,std::vector<std::function<void(constevent&)>>>map_event_handlers;// 互斥锁,用于保证线程安全std::mutex mut;// 运行状态标志,true表示正在运行,false表示已停止boolis_running;public:// 注册处理器:为特定类型的事件注册一个处理函数voidon(inttype,std::function<void(constevent&)>handler);// 循环处理事件:持续从队列中取出事件并处理,直到is_running为falsevoidrun();// 添加事件:将新事件添加到事件队列中voidadd_event(inttype,std::string data);// 处理事件:根据事件类型查找并执行对应的处理函数voidhandle_event(constevent&e);// 停止事件驱动voidstop();};// 注册事件voidEventDrive::on(inttype,std::function<void(constevent&)>handler){map_event_handlers[type].push_back(handler);}// 循环处理事件voidEventDrive::run(){is_running=true;while(is_running){if(queue_events.empty()){continue;}else{std::lock_guard<std::mutex>lock(mut);event e=queue_events.front();queue_events.pop();handle_event(e);}std::this_thread::sleep_for(std::chrono::milliseconds(1));}}// 添加事件voidEventDrive::add_event(inttype,std::string data){std::lock_guard<std::mutex>lock(mut);event e;e.type=type;e.data=data;queue_events.push(e);}// 处理事件voidEventDrive::handle_event(constevent&e){autoit=map_event_handlers.find(e.type);if(it!=map_event_handlers.end()){for(auto&handler:it->second){handler(e);}}}// 停止事件驱动voidEventDrive::stop(){is_running=false;}

使用

1.注册
将处理器以函数的方式注册到处理器队列中。
2.分析线程循环
将事件驱动的循环在主线程外部执行,这样主线程用于输入任务。
3.载入任务
将任务以事件的方式载入任务队列中。

#include"event_drive.hpp"#include<iostream>intmain(){EventDrive envent_drive;//注册事件envent_drive.on(1,[](constevent&e){std::cout<<"handler 1,收到事件1:"<<e.data<<std::endl;});envent_drive.on(2,[](constevent&e){std::cout<<"handler 2,收到事件2:"<<e.data<<std::endl;});envent_drive.on(1,[](constevent&e){std::cout<<"handler 3,也收到事件1:"<<e.data<<std::endl;});// 启动事件循环std::threadloop_thread([&envent_drive](){envent_drive.run();});// 发送事件envent_drive.add_event(1,"data 1");envent_drive.add_event(2,"data 2");envent_drive.add_event(1,"data 3");// 停止事件循环std::this_thread::sleep_for(std::chrono::milliseconds(100));envent_drive.stop();loop_thread.join();return0;}

结果

handler 1,收到事件1:data 1 handler 3,也收到事件1:data 1 handler 2,收到事件2:data 2 handler 1,收到事件1:data 3 handler 3,也收到事件1:data 3

总结

使用事件驱动的方式,将任务抽象为事件,将处理方式抽象为处理器,将任务和处理器注册到事件驱动中,事件驱动循环处理任务。

http://www.jsqmd.com/news/1036032/

相关文章:

  • CodeWarrior IDE编译与链接实战:从源码到可执行文件的构建全解析
  • 2026外贸联合运营哪家好?国内外贸联合运营公司实力盘点 - 栗子测评
  • 3分钟极速上手:Windows上最轻量级的安卓应用安装器终极指南
  • 长沙VI设计品牌推荐
  • 国内有哪些做销售接待过程和对话分析的AI硬件产品?2026年主流方案与选型建议
  • HPC II评估板从开箱到上电:硬件连接、Bootloader调试与Linux启动全解析
  • 衡阳高口碑黄金铂金回收白银回收实体老店排行 5 家靠谱门店电话地址全收录
  • TWR-56F8257开发板硬件解析与实战:DSC电机控制平台设计精髓
  • 终极Flash浏览器指南:如何在现代系统上完美运行经典Flash内容
  • Framer 3.0 高保真原型设计与落地实战指南
  • JN516x红外与I2C/SPI通信外设实战:从原理到避坑指南
  • 嵌入式调试实战:从断点原理到Trace跟踪的深度解析
  • 基于NXP GenAVB栈的AVB/AVDECC音频流配置实战指南
  • 解决 NVIDIA Profile Inspector 配置文件导入失败的 NVAPI_ACCESS_DENIED 错误
  • 2026年众智商学院CPPM采购支出分析与数据驱动决策怎么学?采购数据分析岗位提升路径 - 众智商学院官方
  • zip slip目录遍历漏洞
  • JavaWeb/JSP 项目基础框架:Servlet、JSP、JDBC 与管理系统案例整理
  • IEEE 802.15.4 MAC层服务原语开发实战:基于JN516x的物联网通信指南
  • 合肥 2026收金真实体验:同样50g黄金,不同门店到手价差差出上千 - 奢侈品回收评测
  • ZigBee OTA升级实战:从API函数到安全可靠的无线固件更新
  • 三步构建OFD转PDF自动化工作流:Ofd2Pdf技术解析与实战指南
  • PowerPC 601流水线优化:从数据依赖、旁路技术到实战避坑指南
  • 动态增强采样器:提升图像模型鲁棒性的智能数据增强技术
  • 2026靠谱降AI率软件怎么选?实测15款后这几个最好用
  • ComfyUI ControlNet Aux预处理器:从零开始的完整配置指南
  • 文件上传正常绕过
  • 2026大模型系统化学习路线:从零基础到落地进阶全指南
  • SK-S12XDP512-A开发板硬件配置与调试实战指南
  • 基于NXP i.MX平台的AVB/TSN音视频网络评估实战指南
  • 亨得利官方正式辟谣:关于亨得利服务渠道不实信息的严正声明与权威公示 - 亨得利官方维修中心