news 2026/6/18 13:17:49

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
并发编程(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

总结

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/18 13:11:04

基于NXP KV31F MCU的电机控制评估平台:从硬件设计到FOC算法实践

1. 项目概述&#xff1a;从芯片到板卡&#xff0c;一个高压电机控制评估平台的诞生在工业自动化、家电和新能源汽车等领域&#xff0c;电机控制是核心技术之一。无论是驱动一台空调压缩机&#xff0c;还是控制一台工业机器人的关节&#xff0c;其背后都需要一个实时、精准且可靠…

作者头像 李华
网站建设 2026/6/18 13:01:01

ZigBee时间同步机制深度解析:从ZCL时间集群到工程实践

1. ZigBee时间同步&#xff1a;为什么它比你想象的更复杂 在物联网和无线传感器网络里&#xff0c;让一堆设备“对表”从来都不是一件简单的事。你可能觉得&#xff0c;不就是个时间嘛&#xff0c;设备自己跑个RTC&#xff08;实时时钟&#xff09;不就行了&#xff1f;但在一个…

作者头像 李华
网站建设 2026/6/18 12:54:38

NXP 5685X DSC定时器与GPIO配置实战:从寄存器到电机控制应用

1. 项目概述与核心价值在嵌入式开发领域&#xff0c;尤其是涉及电机控制、电源管理或需要精确时序的工业应用中&#xff0c;Freescale&#xff08;现NXP&#xff09;的5685X系列数字信号控制器&#xff08;DSC&#xff09;是许多工程师的老朋友。这类芯片的核心魅力在于其强大的…

作者头像 李华
网站建设 2026/6/18 12:54:17

DSP5685x SDK库深度解析:从信号处理到安全通信的嵌入式开发实战

1. 项目概述与DSP5685x平台定位在嵌入式系统开发&#xff0c;尤其是涉及实时音频处理、有线通信调制解调或数据安全传输的领域&#xff0c;数字信号处理器&#xff08;DSP&#xff09;扮演着无可替代的角色。这类应用对计算效率和实时性要求极高&#xff0c;通用微控制器&#…

作者头像 李华
网站建设 2026/6/18 12:46:00

模型量化:从 FP16 到 INT4,怎么平衡精度和速度

模型量化&#xff1a;从 FP16 到 INT4&#xff0c;怎么平衡精度和速度 一、量化的本质&#xff1a;用精度换速度的数学基础 模型量化就是把模型参数从高精度浮点数&#xff08;FP32/FP16&#xff09;转成低精度整数&#xff08;INT8/INT4&#xff09;。这么做能减少内存占用和计…

作者头像 李华
网站建设 2026/6/18 12:31:55

PowerPC 601条件循环流水线行为深度解析与优化策略

1. 项目概述与核心价值如果你曾经在嵌入式开发或者对性能有极致要求的实时系统中写过循环代码&#xff0c;那你一定对“流水线停顿”这个词深恶痛绝。一个简单的for或者while循环&#xff0c;在高级语言里看起来人畜无害&#xff0c;但到了处理器内部&#xff0c;尤其是像Power…

作者头像 李华