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

基于C++实现的EventLoop与事件驱动编程

一,概念介绍

事件驱动编程(Event-Driven)是一种编码范式,常被应用在图形用户界面,应用程序,服务器开发等场景。

采用事件驱动编程的代码中,通常要有事件循环,侦听事件,以及不同事件所对应的回调函数。

事件驱动编程经常被应用在前端开发以及C++服务器开发等场景。

Event即事件,是事件驱动编程中的基本处理单元,可以理解为各种各样的信号,对于UI界面来说,鼠标点击、键盘输入、触摸屏输入都可以理解为事件。

事件循环模式(Event loop)是一种简单且高效的并发编程模式,当前业界有很多主流的C++编程框架比如libevent,libuv,Boost.Asio等都支持事件循环机制。但是考虑代码封装上的简洁,我们也可以借助C++11标准实现自己的事件循环代码。通过事件循环,程序可以支持非阻塞的异步操作,提高系统的性能。

步骤示意图:

拿Event填充Event队列:

客户端只管发起请求,触发相应的事件,其他步骤交给队列去处理:

EventLoop样例代码:

#include <algorithm>
#include <iostream>
#include <vector>
#include <map>class EventManager {
private:std::map<std::string, std::vector<void (*)(int)> > events;public:EventManager() {}EventManager* eventRegist(std::string event_name, void (*callback)(int)) {std::vector<void (*)(int)>* listeners = &events[event_name];// if this listener is already registered, we wont add it againif (std::find(listeners->begin(), listeners->end(), callback) !=  listeners->end()) {return this;}listeners->push_back(callback);return this;}bool emit(std::string event_name, int arg) {std::vector<void (*)(int)> listeners = events[event_name];if (listeners.size() == 0) return false;for (int idx = 0; idx < listeners.size(); idx += 1) {listeners[idx](arg);}return true;}
};void callback1(int num) {std::cout << "callback1-" << num << std::endl;
}
void callback2(int num) {std::cout << "callback2-" << num << std::endl;
}int main() {EventManager* event_manager = new EventManager();//注册回调函数event_manager->eventRegist("event1", callback1);event_manager->eventRegist("event2", callback2);//执行回调函数int eventA = event_manager->emit("event1", 10);int eventB = event_manager->emit("event2", 20);return 0;
}

运行结果:

callback1-10
callback2-20

根据以上代码样例,我们发现事件驱动编程通常有以下编码元素:

1.回调函数:回调函数可以是预定义的函数,也可以是匿名函数或Lambda表达式。

2.注册回调:将回调函数赋值给Event的一个std::function成员变量,再将Event添加到Event Loop对应的队列中。

3.触发Event对应的请求以后,从队列中执行事件对应的回调函数。

二,Event Loop步骤拆解

事件循环(Event loop)是一种轮询机制,这种轮询是异步的,有时候轮询和事件注册发生在不同的线程中。

事件循环特别适用于异步编程,在事件循环中,程序会不断地等待事件的发生,并根据事件的类型和优先级来执行相应的处理逻辑。

事件循环主要由以下四个部分组成:

1.事件队列(Event Queue):

用于存储待处理的事件,每个事件都包含一个回调函数和相应的函数参数。

2.事件触发器(Event Trigger):

负责监听外部事件(如用户输入、网络请求等),并将事件添加到事件队列中。

3.事件处理器(Event Handler):

从事件队列中取出对应事件,并执行事件的回调函数。

4.回调函数(Callback Function):

与特定事件相关联的函数,当对应的事件发生时才会被调用执行。回调函数只有被"注册"到事件队列中才会被调用执行。所谓的"注册"就是将回调函数赋值给Event对应的函数对象。

事件循环(Event Loop)是一个无限循环,它会不断地从事件队列中取出事件,并执行对应的回调函数。在有些模式下,事件循环会检查事件队列是否为空,如果为空则会进入休眠状态等待新的事件到来。

c++服务器开发教程

【腾讯T9推荐】2024最新linux c/c++后端服务器开发教程,通俗易懂深入底层讲解,多项目实战,1V1指导,学完轻松拿下大厂offer!!!icon-default.png?t=N7T8https://www.bilibili.com/video/BV1XZ421q7UD/

免费学习地址:c/c++ linux服务器开发/后台架构师

需要C/C++ Linux服务器架构师学习资料加qun579733396获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

事件循环的基本流程如下:

step.01:初始化事件队列。

step.02:进入循环,等待事件的发生。

step.03:当监听的事件被触发时,将事件添加到事件队列中。

step.04:从事件队列中取出一个事件,并异步执行对应的回调函数。

step.05:返回第2步,继续等待下一个事件的发生。

注意:step.01~step.05并不只发生在同一个线程中,很多时候,回调函数的调用会放在子线程中进行。

参考以上步骤,我们可以设计以下Event Loop代码:

#include <condition_variable>
#include <functional>
#include <thread>
#include <vector>
#include <iostream>class EventLoop
{
public:using callable_t = std::function<void()>;EventLoop() = default;~EventLoop() noexcept{enqueue([this]{m_running = false;});std::cout << "step.02: other thread print.\n";m_thread.join();}//禁用移动构造 & 拷贝构造EventLoop(const EventLoop&) = delete;EventLoop(const EventLoop&&) = delete;EventLoop& operator= (const EventLoop&) = delete;EventLoop& operator= (const EventLoop&&)  = delete;void enqueue(callable_t&& callable) noexcept{{std::lock_guard<std::mutex> guard(m_mutex);m_writeBuffer.emplace_back(std::move(callable));}m_condVar.notify_one();}
private:std::vector<callable_t> m_writeBuffer;std::mutex m_mutex;std::condition_variable m_condVar;bool m_running{ true };std::thread m_thread{ &EventLoop::threadFunc, this};void threadFunc() noexcept{std::vector<callable_t> readBuffer;while (m_running){{std::unique_lock<std::mutex> lock(m_mutex);m_condVar.wait(lock, [this]{return !m_writeBuffer.empty();});std::swap(readBuffer, m_writeBuffer);}for (callable_t& func : readBuffer){func();}readBuffer.clear();}std::cout << "step.03: event loop end.\n";}
};int main()
{EventLoop eventLoop;eventLoop.enqueue([]{std::cout << "Event_01 is running.\n";});eventLoop.enqueue([]{std::cout << "Event_02 is running.\n";});eventLoop.enqueue([]{std::cout << "Event_03 is running.\n";});std::cout << "step.01: main thread print.\n";
}

运行结果:

step.01: main thread print.
step.02: other thread print.
Event_01 is running.
Event_02 is running.
Event_03 is running.
step.03: event loop end.

三,事件驱动代码实战

Demo1:没有添加Event Loop,主要运行Callback回调函数

#include <iostream>
#include <functional>
#include <string>
// 定义回调函数类型
typedef std::function<void(std::string str)> Callback;
// 模拟用户输入事件
void simulateUserInput(Callback callback_func) {std::string input;std::cout << "请输入一段文字:";getline(std::cin, input);callback_func(input);  // 触发回调函数
}
// 处理用户输入事件的回调函数
void handleUserInput(std::string inputStr) {std::cout << "用户输入事件已触发!" << std::endl;std::cout << "用户输入的是: " << inputStr << std::endl;return;
}int main() {simulateUserInput(handleUserInput);return 0;
}

运行结果:

请输入一段文字:hello
用户输入事件已触发!
用户输入的是:hello

Demo2:

#include <iostream>
#include <functional>
#include <queue>
//定义事件类型
typedef std::function<void()> Event;
//事件队列
std::queue<Event> eventQueue;
//注册回调函数
void registerEventHandler(Event event) {eventQueue.push(event);
}
//事件处理器
void processEvents() {while (!eventQueue.empty()) {Event event = eventQueue.front();event();  //调用回调函数eventQueue.pop();}
}
//回调函数
void callback1() {std::cout << "Callback 1 called" << std::endl;
}
void callback2() {std::cout << "Callback 2 called" << std::endl;
}
int main() {//注册回调函数到事件队列registerEventHandler(callback1);registerEventHandler(callback2);//处理事件processEvents();return 0;
}

运行结果:

Callback 1 called
Callback 2 called

Demo3:

#include <iostream>
#include <functional>
#include <queue>
//定义Event结构体
struct Event {std::function<void()> callback;
};
//定义事件处理器
class EventHandler {
public:void handleEvent(Event event) {event.callback();}
};
//定义事件循环
class EventLoop {
public:void addEvent(Event event) {eventQueue.push(event);}void run() {while (!eventQueue.empty()) {Event event = eventQueue.front();eventQueue.pop();eventHandler.handleEvent(event);}}
private:std::queue<Event> eventQueue;EventHandler eventHandler;
};
int main() {//创建事件循环对象EventLoop eventLoop;//回调函数std::function<void()> callback1 = []() {std::cout << "Event 1 triggered!" << std::endl;};std::function<void()> callback2 = []() {std::cout << "Event 2 triggered!" << std::endl;};//创建事件并添加到事件循环中Event event1{ callback1 };Event event2{ callback2 };eventLoop.addEvent(event1);eventLoop.addEvent(event2);//运行事件循环eventLoop.run();return 0;
}

运行结果:

Event 1 triggered!
Event 2 triggered!

相关文章:

基于C++实现的EventLoop与事件驱动编程

一&#xff0c;概念介绍 事件驱动编程&#xff08;Event-Driven&#xff09;是一种编码范式&#xff0c;常被应用在图形用户界面&#xff0c;应用程序&#xff0c;服务器开发等场景。 采用事件驱动编程的代码中&#xff0c;通常要有事件循环&#xff0c;侦听事件&#xff0c;…...

Android高级面试_8_热修补插件化等

Android 高级面试&#xff1a;插件化和热修复相关 1、dex 和 class 文件结构 class 是 JVM 可以执行的文件类型&#xff0c;由 javac 编译生成&#xff1b;dex 是 DVM 执行的文件类型&#xff0c;由 dx 编译生成。 class 文件结构的特点&#xff1a; 是一种 8 位二进制字节…...

显卡GTX与RTX有什么区别?哪一个更适合玩游戏?

游戏发烧友们可能对游戏显卡并不陌生&#xff0c;它直接关系到游戏画面的流畅度、细腻程度和真实感。在众多显卡品牌中&#xff0c;英伟达的GTX和RTX系列显卡因其出色的性能而备受关注。 一、GTX与RTX的区别 架构差异 GTX系列显卡采用的是Pascal架构&#xff0c;这是英伟达在…...

QT自定义信号和槽函数

在QT中最重要也是必须要掌握的机制&#xff0c;就是信号与槽机制&#xff0c;在MFC上也就是类型的机制就是消息与响应函数机制 在QT中我们不仅要学会如何使用信号与槽机制&#xff0c;还要会自定义信号与槽函数&#xff0c;要自定义的原因是系统提供的信号&#xff0c;在一些情…...

Atcoder Beginner Contest 359

传送门 A - Count Takahashi 时间限制&#xff1a;2秒 内存限制&#xff1a;1024MB 分数&#xff1a;100分 问题描述 给定 N 个字符串。 第 i 个字符串 () 要么是 Takahashi 要么是 Aoki。 有多少个 i 使得 等于 Takahashi &#xff1f; 限制 N 是整数。每个…...

无线通讯几种常规天线类别简介

天线对于无线模块来说至关重要&#xff0c;合适的天线可以优化通信网络&#xff0c;增加其通信的范围和可靠性。天线的选型对最后的模块通信影响很大&#xff0c;不合适的天线会导致通信质量下降。针对不同的市场应用&#xff0c;天线的材质、安置方式、性能也大不一样。下面简…...

最大团问题--回溯法

一、相关定义 给定一个无向图 &#xff0c;其中 V 是图的顶点集&#xff0c;E图的边集 完全图&#xff1a;如果无向图中的任何一对顶点之间都有边&#xff0c;这种无向图称为完全图 完全子图&#xff1a;给定无向图 &#xff0c;如果 &#xff0c;且对应任意 且 &#xff0c;则…...

MBSE之简单介绍

MBSE之简单介绍 文章目录 MBSE之简单介绍1. What is MBSE&#xff1f;2. MBSE 最佳实践 1. What is MBSE&#xff1f; Model-Based Systems Engineering (MBSE), a.k.a. Model-Based Systems Development (MBSD), is a Systems Engineering process paradigm that emphasizes t…...

基于ODPS解析字段值为JSON的情况

最近在使用ODPS数据库&#xff0c;其中一个字段他是用JSON存储的&#xff0c;但是我是需要JSON字符串中的一个属性值就行&#xff0c;刚好ODPS中有一个函数可以用来使用! 使用案例 select GET_JSON_OBJECT({"id":1,"name":"xiaobai"},$.name);…...

CesiumJS【Basic】- #020 加载glb/gltf文件(Primitive方式)

文章目录 加载glb/gltf文件(Primitive方式)1 目标2 代码实现3 资源文件加载glb/gltf文件(Primitive方式) 1 目标 使用Primitive方式加载glb/gltf文件 2 代码实现 import * as Cesium from "cesium";const viewer = new Cesium.Viewer...

2024黑盾杯复现赛题MISC部分

一、一个logo 一张png图片&#xff0c;查看颜色通道即可发现flag 二、 学会Office 最好用联想自带的excel工具查看&#xff0c;我用WPS打开未解出题目 这里会发现有隐藏信息 隐藏信息为宏加密 。去百度了解宏加密后&#xff0c;发现有俩个宏&#xff0c;一个加密一个解密 执…...

Linux0.12内核源码解读(5)-head.s

大家好&#xff0c;我是呼噜噜&#xff0c;好久没有更新old linux了&#xff0c;本文接着上一篇文章图解CPU的实模式与保护模式&#xff0c;继续向着操作系统内核的世界前进&#xff0c;一起来看看heads.s as86 与GNU as 首先我们得了解一个事实&#xff0c;在Linux0.12内核源…...

刷代码随想录有感(119):动态规划——打家劫舍III(树形dp)

题干&#xff1a; 代码&#xff1a; class Solution { public:vector<int>dp(TreeNode* cur){if(cur NULL)return vector<int>{0, 0};vector<int> left dp(cur -> left);vector<int> right dp(cur -> right);//偷int val1 cur -> val l…...

vivado CARRY_REMAP、CASCADE_HEIGHT

CARRY_REMAP opt_design-carry_remap选项可用于将单个carry*单元重新映射到LUT中 提高了布线的设计效果。使用-carry_remap选项时&#xff0c;仅 将单级进位链转换为LUT。CARRY_REMAP属性允许您 指定在优化过程中要转换的长度较大的进位链。 您可以使用控制任意长度的单个进位链…...

Ubuntu磁盘分区和挂载 虚拟机扩容 逻辑卷的创建和扩容保姆及教程

目录 1、VMware虚拟机Ubuntu20.04系统磁盘扩容 2、Linux的磁盘分区和挂载 3、创建逻辑卷和逻辑卷的扩容 1、VMware虚拟机Ubuntu20.04系统磁盘扩容 通过下图可以看出我们的根磁盘一共有20G的大小&#xff0c;现在我们把它扩容为30G 注&#xff1a;如果你的虚拟机有快照是无…...

【附精彩文章合辑】哈佛辍学小哥的创业经历【挑战英伟达!00 后哈佛辍学小哥研发史上最快 AI 芯片,比 H100 快 20 倍!】

前情提要 https://blog.csdn.net/weixin_42661676/article/details/140020491 哈佛辍学小哥的创业经历 一、背景与起步 这位哈佛辍学小哥&#xff0c;名为Chris Zhu&#xff0c;是一位华裔学生&#xff0c;他在2020年进入哈佛大学&#xff0c;攻读数学学士学位和计算机科学硕…...

Oracle CPU使用率过高问题处理

1.下载Process Explorer 2.打开Process Explorer&#xff0c;查看CPU使用情况最高的进程 3.双击该进程&#xff0c;查看详情 \ 4. 获取cpu使用最好的线程tid 5. 查询sql_id select sql_id from v$session where paddr in( select addr from v$process where spid in(1…...

pyqt的QWidgetList如何多选?如何按下Ctrl多选?

通过设置setSelectionMode(QAbstractItemView.MultiSelection)&#xff0c;可以实现QWidgetList的多选。 但是上述结果不太符合我们需求。设置多选模式后&#xff0c;只需鼠标点击就可以选择多个条目。 我希望按下Ctrl键时才进行多选&#xff0c;仅鼠标单击的话&#xff0c;只进…...

【电路笔记】-MOSFET放大器

MOSFET放大器 文章目录 MOSFET放大器1、概述2、电路图3、电气特性3.1 ** I D = F ( V G S ) I_D=F(V_{GS}) ID​=F(VGS​)**特性3.2 I D = F ( V D S ) I_D=F(V_{DS}) ID​=F(VDS​)特性4、MOSFET放大器5、输入和输出电压6、电压增益7、总结1、概述 在前面的文章中,我们已经…...

Ubuntu 20.04安装显卡驱动、CUDA、Pytorch(2024.06最新)

文章目录 一、安装显卡驱动1.1 查看显卡型号1.2 根据显卡型号选择驱动1.3 获取下载链接1.4 查看下载的显卡驱动安装文件1.5 更新软件列表和安装必要软件、依赖1.6 卸载原有驱动1.7 禁用默认驱动1.8 安装lightdm显示管理器1.9 停止显示服务器1.10 在文本界面中&#xff0c;禁用X…...

wpf 附加属性 RegisterAttached 内容属性

// // 摘要: // 选中时展示的元素 public static readonly DependencyProperty CheckedElementProperty DependencyProperty.RegisterAttached("CheckedElement", typeof(object), typeof(StatusSwitchElement), new PropertyMetadata((object)null…...

laravel8框架windows下安装运行

目录 1、安装前如果未安装先安装Composer 2、使用composer安装laravel8 3、使用内置服务器:8000 的命令去访问测试 ​4、使用本地环境运行phpstudy配置到public目录下 Laravel官网 Laravel 中文网 为 Web 工匠创造的 PHP 框架 安装 | 入门指南 |《Laravel 8 中文文档 8.x…...

如何快速判断IP被墙

IP被墙是指IP部分地区或者运营商无法被正常进行访问的一个情况。 被墙的原因有很多种不一一列举&#xff0c;由于被墙的时间短的为按周按月计算&#xff0c;时间长的则为按年计算&#xff0c;所以一般这种情况下只能选择更换IP。 检查办法&#xff1a; 第一&#xff0c;确认IP…...

vitest-前端单元测试

Vitest是一个轻量级、快速且功能强大的测试框架&#xff0c;特别适用于Vite项目&#xff0c;但也可以与其他前端项目&#xff08;如使用webpack构建的项目&#xff09;集成使用。Vitest提供极速的测试体验&#xff0c;并包含一系列用于编写和组织测试用例的API&#xff0c;如de…...

Redis 7.x 系列【9】数据类型之自动排重集合(Set)

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 前言2. 常用命令2.1 SADD2.2 SCARD2.3 SISMEMBER2.4 SREM2.5 SSCAN2.6 SDIFF2.7 SU…...

【LeetCode】每日一题:反转链表

题解思路 循环的方法需要注意prev应该是None开始&#xff0c;然后到结束的时候prev是tail&#xff0c;递归的思路很难绕过弯来&#xff0c;主要在于很难想清楚为什么可以返回尾节点&#xff0c;需要多做递归题&#xff0c;以及递归过程中&#xff0c;可以不使用尾节点来找当前…...

使用Spring Boot创建自定义Starter

使用Spring Boot创建自定义Starter 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何使用Spring Boot创建自定义Starter&#xff0c;来简化项目…...

cmd设置编码为utf8

文章目录 临时设置永久设置(通过注册表) cmd命令乱码&#xff0c;解决方案比较简单。 输入chcp&#xff0c; 如果返回的是936&#xff0c;通常是GBK或CP936。 如果返回的是65001&#xff0c;表示是UTF-8。 临时设置 chcp 65001 # 设置 chcp # 查看 永久设置(通过注册表) 打…...

一次关于k8s的node节点NotReady的故障排查

master现象 分析 kubectl get nodes -A 看了下pod的状态&#xff0c;好多CrashLoopBackOff kubectl get nodes -o wide 定位到那个具体node的IP地址&#xff0c;登录对应的IP去查看为什么会这样 node节点 journalctl -xe -f -u kubelet 查看此节点的 kubelet 服务&#xff…...

Java变量与标识符

一、关键字&#xff08;Keyboard&#xff09; 定义&#xff1a;被Java语言赋予了特殊含义&#xff0c;用做专门用途的字符串&#xff08;或单词&#xff09; 特点&#xff1a;全部关键字都是小写字母 官方地址&#xff1a; https://docs.oracle.com/javase/tutorial/java/nut…...

AWS无服务器 应用程序开发—第十七章 AWS用户池案例

在AWS Cognito用户池中&#xff0c;用户属性可以根据应用程序的需求进行配置和管理。以下是一般情况下用户属性的一些常见设置&#xff1a; 必须的属性&#xff1a; 用户名&#xff08;Username&#xff09;&#xff1a;通常用作用户的唯一标识符。 密码&#xff08;Password…...

java中的枚举

第1部分&#xff1a;引言 枚举在Java中的重要性 枚举在Java中扮演着至关重要的角色&#xff0c;它不仅提高了代码的可读性和可维护性&#xff0c;还增强了类型安全。枚举的使用可以避免使用魔法数字或散列常量&#xff0c;这些在代码中通常难以理解和维护。通过枚举&#xff…...

各种开发语言运行时占用内存情况比较

随着科技的发展&#xff0c;编程语言种类繁多&#xff0c;不同的编程语言在运行时的内存占用情况各不相同。了解这些差异对于开发者选择合适的编程语言尤为重要。本文将讨论几种主流编程语言在运行时的内存占用情况&#xff0c;包括C、C、Java、Python和Go等。 1. C语言 内存…...

【基础知识10】label与input标签

label标签说明 HTML元素表示用户界面中某个元素的说明 将一个和一个元素相关联主要有这些优点&#xff1a; 标签文本不仅与其相应的文本输入元素在视觉上相关联&#xff0c;程序中也是如此。这意味着&#xff0c;当用户聚焦到这个表单输入元素时&#xff0c;屏幕阅读器可以读…...

【SDV让汽车架构“和而不同”】

昔日以“排气管数量”和“发动机动力”为骄傲的荣耀已然成为过往。在这个崭新的时代&#xff0c;特斯拉、理想、蔚来、小鹏、零跑等新兴的汽车制造商纷纷推出了搭载可交互大屏、实现万物互联、软件功能持续更新的新车型&#xff0c;它们被誉为“车轮上的智能手机”。同时&#…...

面试经验分享 | 驻场安全服务工程师面试

所面试的公司&#xff1a;某安全厂商 所在城市&#xff1a;浙江宁波 面试职位&#xff1a;驻场安全服务工程师 面试官的问题&#xff1a; 1、信息收集如何处理子域名爆破的泛解析问题&#xff1f; 泛域名解析是&#xff1a;*.域名解析到同一IP。域名解析是&#xff1a;子域…...

SpringBoot 学习笔记

文章目录 SpringBoot1 SpringBoot 的纯注解配置&#xff08;了解&#xff09;1.1 环境搭建1.1.1 jdbc配置1.1.2 mybatis配置1.1.3 transactional配置1.1.4 service配置1.1.5 springmvc配置1.1.6 servlet配置1.1.7 存在的问题 1.2 新注解说明1.2.1 Configuration1.2.2 Component…...

Android 13 为应用创建快捷方式

参考 developer.android.google.cn 创建快捷方式 来自官网的说明&#xff1a; 静态快捷方式 &#xff1a;最适合在用户与应用互动的整个生命周期内使用一致结构链接到内容的应用。由于大多数启动器一次仅显示四个快捷方式&#xff0c;因此静态快捷方式有助于以一致的方式执行…...

PTA—C语言期末复习(选择题)

1. 按照标识符的要求&#xff0c;&#xff08;A&#xff09;不能组成标识符。 A.连接符 B.下划线 C.大小写字母 D.数字字符 在大多数编程语言中&#xff0c;标识符通常由字母&#xff08;包括大写和小写&#xff09;、数字和下划线组成&#xff0c;但不能以数字开头&#xff0c…...

基于STM32的智能家用空气净化系统

目录 引言环境准备智能家用空气净化系统基础代码实现&#xff1a;实现智能家用空气净化系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统实现4.4 用户界面与数据可视化应用场景&#xff1a;空气净化管理与优化问题解决方案与优化收尾与总结 1. 引言 智能家用空气净化系…...

计算机图形学入门18:阴影映射

1.前言 前面几篇关于光栅化的文章中介绍了如何计算物体表面的光照&#xff0c;但是着色并不会进行阴影的计算&#xff0c;阴影需要单独进行处理&#xff0c;目前最常用的阴影计算技术之一就是Shadow Mapping技术&#xff0c;也就是俗称的阴影映射技术。 2.阴影映射 Shadow Map…...

电机应用相关名词介绍

1.电机转速 定义&#xff1a;电机转速指电机工作时旋转的速度&#xff0c;是衡量电机性能的重要指标之一。 单位&#xff1a; 每分钟转数&#xff08;RPM&#xff09;&#xff1a;即Revolutions Per Minute&#xff0c;表示电机每分钟旋转的圈数。 每秒转数&#xff08;RPS…...

哈尔滨等保测评解读

哈尔滨的信息系统安全等级保护测评&#xff08;简称“等保测评”&#xff09;是中国网络安全法规的一部分&#xff0c;旨在确保关键信息基础设施和其他重要信息系统的安全。下面是对哈尔滨等保测评的解读&#xff1a; 测评目的 等保测评的主要目的是评估信息系统是否满足国家规…...

python接口自动化的脚本

使用Requests库进行GET请求 Requests是Python中最常用的HTTP库,用于发送HTTP请求。下面是一个简单的GET请求示例,用于从API获取数据。 import requests url = "https://api.example.com/data" response = requests.get(url) if response.status_code == 200:prin…...

pdf转换成cad,这几个cad转换小妙招快码住!

在数字设计领域&#xff0c;PDF&#xff08;Portable Document Format&#xff09;和CAD&#xff08;Computer-Aided Design&#xff09;文件格式各有其独特之处。PDF常用于文件共享和打印&#xff0c;而CAD则是工程师和设计师们进行精确绘图和建模的必备工具。然而&#xff0c…...

计算机组成原理——系统总线

题目:计算机使用总线结构便于增减外设,同时__C____。 A.减少了信息传送量 B.提高了信息传输速度 C.减少了信息传输线的条数 1. 总线的分类 1.1. 片内总线 芯片内部的总线 在CPU芯片内部,寄存器与寄存器之间、寄存器与逻辑单元ALU之间 1.1.1. 数据总线 双向传输总线 数…...

2024年6月大众点评广州餐饮店铺POI分析20万家

2024年6月大众点评广州餐饮店铺POI共有199175家 店铺POI点位示例&#xff1a; 店铺id k9uiFADtAvs9EdPC 店铺名称 点都德(聚福楼店) 十分制服务评分 8.6 十分制环境评分 8.3 十分制划算评分 8.5 人均价格 77 评价数量 41673 店铺地址 惠福东路470号(富临食府对面) 大…...

【最佳实践】前端如何搭建自己的cli命令行工具,让自己编码的时候如虎添翼

作为前端开发人员&#xff0c;搭建自己的前端CLI工具是一个有趣且有意义的事情。以下是一篇详细的教程&#xff0c;包括使用场景和案例。 使用场景 假设你是一个前端团队的一员&#xff0c;需要频繁地在不同的项目中执行一些标准化的任务&#xff0c;比如&#xff1a; 根据模…...

未来一周比特币价格及数字货币市场预测

荷月的比特币市场就像过山车一样&#xff0c;仅仅六月下旬就跌去-12%&#xff0c;本周更是暴跌-6%&#xff0c;至 58,378美元。在这种市场表现&#xff0c;应有的踩踏如期而至。德国政府今日宣布再出售750 比特币的行为继续打击多头&#xff0c;但是小编认为这恰恰预示着市场可…...

Qt Quick 教程(二)

文章目录 今天分析一段代码1. 注册单例类型2. 注册普通QML类型3. 注册C++类型到Qt元对象系统4.总结,具体解释5.如何在QML中使用这些注册的类型参考今天分析一段代码 // Register typesqmlRegisterSingletonType(QUrl("qrc:/StyleSheet.qml"), "Librum.style&qu…...