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

Qt Signals Slots VS QEvents - Qt跨线程异步操作性能测试与选取建议

相关代码参考:https://gitcode.net/coloreaglestdio/qtcpp_demo/-/tree/master/qt_event_signal

1.问题的由来

在对 taskBus 进行低延迟改造时,避免滥用信号与槽起到了较好的作用。笔者在前一篇文章中,叙述了通过避免广播式地播发信号,以及频繁的 new 与 delete 来提高软件无线电(SDR)平台的吞吐。近期,考虑到跨线程异步操作其实事件(QEvent)可能更加适合点对点的调用,遂把taskBus的主数据流转使用 Events 进行了改造,收到了大概5-10ms的提升。

这个提升还是没有达到我的期望,因为印象里,信号与槽是非常慢的。那么,问题来了,跨线程 Signal&Slots 与 Events 到底谁要快,为什么快,以及快多少呢?

2. 回顾“信号槽很慢”的印象的源头

在经典的Qt文档里,有一段对信号与槽性能选择的性能描述:

Compared to callbacks, signals and slots are slightly slower because of the 
increased flexibility they provide, although the difference for real applications 
is insignificant. In general, emitting a signal that is connected to some slots,is approximately ten times slower than calling the receivers directly, with non-virtual function calls.

“与回调相比,信号和插槽的速度稍慢,因为它们提供了更大的灵活性,尽管实际应用程序的差异并不显著。通常,发射连接到某些插槽的信号比直接调用接收器(使用非虚拟函数调用)慢大约十倍。”

这就是印象的源头了。不过,带着上述问题,仔细阅读,发现还有更多的解释:

 This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission), and to marshall any parameters in a generic fashion. While ten non-virtual function calls may sound like a lot, it's much less overhead than any new or delete operation, for example.As soon as you perform a string, vector or list operation that behind the scene requires new or delete, the signals and slots overhead is only responsible for a very small proportion of the complete function call costs. The same is true whenever you do a system call in a slot; or indirectly call more than ten functions. The simplicity and flexibility of the signals and slots mechanism is well worth the overhead, which your users won't even notice.

“这是定位连接对象、安全地迭代所有连接(即检查后续接收器在发射过程中是否未被破坏)以及以通用方式整理任何参数所需的开销。虽然十个非虚拟函数调用听起来可能很多,但它的开销比任何new操作或delete操作都要小得多。一旦执行了一个字符串、向量或列表操作,而该操作在后台需要新建或删除,则信号和插槽开销只占整个函数调用成本的一小部分。每当您在插槽中进行系统调用时,情况也是如此;或者间接调用十多个函数。信号和插槽机制的简单性和灵活性非常值得开销,而您的用户甚至不会注意到这一点。”

会不会,上次的优化起到关键作用的不是信号与槽改成了直接函数调用,而是把频繁new delete换成静态内存导致的提升?我立刻进行了测试,发现的确如此。

  • new 和 delete的开销远远大于 signal&slots的开销

3. 编程进行专门测试

我们使用Qt专门对信号与槽、事件这两种跨线程传递消息的方法来进行测试。设计测试由两个对象之间互相以最快速度乒乓消息为场景,如下图所示:

时戳消息
时戳消息
Object1
Object2

含有时戳的消息,可以用来计算和统计平均延迟(消息生成和被处理的时差),以及最终的处理能力。

测试分为信号-槽测试,事件测试,以及单线程对比调用测试。测试进行10000次调用,并模拟实际程序对传递的消息进行一些处理,如产生一定长度的字符串。

通过观察不同长度下的开销,即可直挂感受消息传递开销与处理开销的占比。

3.1 用于测试的消息体

消息体既用于信号与槽测试,也用于Event测试以及直接调用。这样参数中都直接new消息,对大家是公平的。

testevent.h---------------#include <QEvent>
#include <time.h>
#include <QString>
class TestMsg : public QEvent
{
private:static QEvent::Type m_testEvt;static QEvent::Type m_startEvt;static QEvent::Type m_startSig;static QEvent::Type m_quit;clock_t m_clk = 0;QString m_dummyLongStr;
public:TestMsg(clock_t clk);~TestMsg();inline clock_t clock() {return m_clk;}inline void fillStr(int len){for (int i=0;i<len;++i){m_dummyLongStr.push_back((char)(i*37%64+32));}}
public:static inline QEvent::Type type() {return m_testEvt;}static inline QEvent::Type startEvt() {return m_startEvt;}static inline QEvent::Type startSig() {return m_startSig;}static inline QEvent::Type quitEvt() {return m_quit;}
};//testevent.cpp---------------
#include "testevent.h"
#include <QDebug>
//Regisit a event
QEvent::Type TestMsg::m_testEvt = (QEvent::Type)QEvent::registerEventType();
QEvent::Type TestMsg::m_startEvt = (QEvent::Type)QEvent::registerEventType();
QEvent::Type TestMsg::m_startSig = (QEvent::Type)QEvent::registerEventType();
QEvent::Type TestMsg::m_quit = (QEvent::Type)QEvent::registerEventType();TestMsg::TestMsg(clock_t clk):QEvent(m_testEvt),m_clk(clk)
{}
TestMsg::~TestMsg()
{}

消息体含有一个clock时戳,用于计算传递耗时。同时,有一个fillStr的耗时操作,用于模拟真实的有效数据处理。

3.2 测试对象

测试对象直接派生自 QObject,将在线程中执行测试。为了模拟信号与槽的 meta 开销,设置了26组信号与槽,进行交叉连接,并用第16组进行测试。

#ifndef TESTOBJ_H
#define TESTOBJ_H#include <QObject>
#include <QEvent>
#include "testevent.h"
class TestObj : public QObject
{Q_OBJECT
public:explicit TestObj(QObject *parent = nullptr);void setBuddy(TestObj * buddy) {m_buddy = buddy;buddy->m_buddy = this;}void runDirectCall();
public slots:void test_slot1(QEvent * evt){}//...void test_slot16(QEvent * evt);//...void test_slot26(QEvent * evt){}
signals:void test_sig1(QEvent * evt);//...void test_sig26(QEvent * evt);void evt_finished();void sig_finished();
protected:void customEvent(QEvent *) override;
private:clock_t m_nFirstClkSig = -1;quint32 m_nCount_Sigs = 0;clock_t m_nFirstClkEvt = -1;quint32 m_nCount_Evts = 0;clock_t m_nFirstClkDir = -1;quint32 m_nCount_Dir = 0;
private:TestObj * m_buddy = nullptr;
private:void run_signal();void run_event();
private:void direct_call(QEvent * evt);
};#endif // TESTOBJ_H

无论以何种接口获得 TestMsg,都执行相同的操作:

//以信号槽为例
void TestObj::test_slot16(QEvent * evt)
{clock_t curr_clk = clock();if (m_nFirstClkSig==-1)m_nFirstClkSig = curr_clk;TestMsg * e = dynamic_cast<TestMsg *>(evt);if (e){++m_nCount_Sigs;e->fillStr(fillStrLen);if (m_nCount_Sigs==testCounts){QTextStream strm(stdout);strm << objectName()<< QString().asprintf(" (%llX) run %d Signals, total costs %.2lf ms, AVG cost %.2lf us / test.\n",(unsigned long long)this,(int)(m_nCount_Sigs),1e3 * (curr_clk - m_nFirstClkSig) / CLOCKS_PER_SEC,1e6 * (curr_clk - m_nFirstClkSig) / CLOCKS_PER_SEC / testCounts);strm.flush();emit sig_finished();}delete e;}
}void TestObj::run_signal()
{for (int i=0;i<testCounts;++i)emit test_sig16(new TestMsg(clock()));
}

3.3 测试方法

在 main函数中进行测试:

#include <QCoreApplication>
#include <QThread>
#include "testevent.h"
#include "testobj.h"
int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);clock_t clk_start = clock();printf("StartClk = %d\n",clk_start);QThread::msleep(100);QThread * thread1 = new QThread;QThread * thread2 = new QThread;TestObj * obj1 = new TestObj;TestObj * obj2 = new TestObj;obj1->setObjectName("OBJ1");obj2->setObjectName("OBJ2");obj1->setBuddy(obj2);obj1->connect(obj1,&TestObj::test_sig1,obj2,&TestObj::test_slot16,Qt::QueuedConnection);//...obj1->connect(obj1,&TestObj::test_sig26,obj2,&TestObj::test_slot16,Qt::QueuedConnection);obj1->connect(obj2,&TestObj::test_sig16,obj1,&TestObj::test_slot1,Qt::QueuedConnection);//...obj1->connect(obj2,&TestObj::test_sig16,obj1,&TestObj::test_slot26,Qt::QueuedConnection);obj1->moveToThread(thread1);obj2->moveToThread(thread2);thread1->start();thread2->start();printf("Test signals & slots, Events...\n");QCoreApplication::processEvents();a.connect (obj1,&TestObj::sig_finished,[=]()->void{QThread::msleep(1000);QCoreApplication::postEvent(obj1,new QEvent(TestMsg::startEvt()));});a.connect (obj2,&TestObj::sig_finished,[=]()->void{QThread::msleep(1000);QCoreApplication::postEvent(obj2,new QEvent(TestMsg::startEvt()));});a.connect (obj1,&TestObj::evt_finished,[=]()->void{QThread::msleep(1000);QCoreApplication::postEvent(obj1,new QEvent(TestMsg::quitEvt()));});a.connect (obj2,&TestObj::evt_finished,[=]()->void{QThread::msleep(1000);QCoreApplication::postEvent(obj2,new QEvent(TestMsg::quitEvt()));});QThread::msleep(1000);QCoreApplication::processEvents();QCoreApplication::postEvent(obj1,new QEvent(TestMsg::startSig()));QCoreApplication::postEvent(obj2,new QEvent(TestMsg::startSig()));a.processEvents();thread1->wait();thread2->wait();QThread::msleep(2000);obj1->runDirectCall();printf("Finished.\n");thread1->deleteLater();thread2->deleteLater();obj1->deleteLater();obj2->deleteLater();QThread::msleep(1000);QCoreApplication::processEvents();return 0;
}

4. 测试结果

测试环境:i7 10代 win11 Mingw64 Qt6.6

4.1 Debug, Strlen=0,20000次

StartClk = 2
Test signals & slots, Events...
OBJ1 (1AD52AD73E0) run 20000 Signals, total costs 2089.00 ms, AVG cost 104.45 us / test.
OBJ2 (1AD52AD74C0) run 20000 Signals, total costs 32.00 ms, AVG cost 1.60 us / test.
OBJ2 (1AD52AD74C0) run 20000 Events, total costs 14.00 ms, AVG cost 0.70 us /test.
OBJ1 (1AD52AD73E0) run 20000 Events, total costs 27.00 ms, AVG cost 1.35 us /test.
Test Direct Call...
OBJ1 (1AD52AD73E0) run 20000 Direct Calls, total costs 6.00 ms, AVG cost 0.30 us / test.
Finished.

4.2 Debug, Strlen=1000,20000次

StartClk = 2
Test signals & slots, Events...
OBJ1 (21AFB83BBA0) run 20000 Signals, total costs 2541.00 ms, AVG cost 127.05 us / test.
OBJ2 (21AFB83B820) run 20000 Signals, total costs 1630.00 ms, AVG cost 81.50 us / test.
OBJ2 (21AFB83B820) run 20000 Events, total costs 1373.00 ms, AVG cost 68.65 us /test.
OBJ1 (21AFB83BBA0) run 20000 Events, total costs 1403.00 ms, AVG cost 70.15 us /test.
Test Direct Call...
OBJ1 (21AFB83BBA0) run 20000 Direct Calls, total costs 1377.00 ms, AVG cost 68.85 us / test.
Finished.

4.3 Release, Strlen=0,100000次

StartClk = 5
Test signals & slots, Events...
OBJ1 (294D24BB5F0) run 100000 Signals, total costs 2236.00 ms, AVG cost 22.36 us / test.
OBJ2 (294D24BB930) run 100000 Signals, total costs 25.00 ms, AVG cost 0.25 us / test.
OBJ2 (294D24BB930) run 100000 Events, total costs 12.00 ms, AVG cost 0.12 us /test.
OBJ1 (294D24BB5F0) run 100000 Events, total costs 38.00 ms, AVG cost 0.38 us /test.
Test Direct Call...
OBJ1 (294D24BB5F0) run 100000 Direct Calls, total costs 14.00 ms, AVG cost 0.14 us / test.
Finished.

4.4 Release, Strlen=1000,100000次

StartClk = 4
Test signals & slots, Events...
OBJ1 (154799851A0) run 100000 Signals, total costs 1358.00 ms, AVG cost 13.58 us / test.
OBJ2 (154799854E0) run 100000 Signals, total costs 620.00 ms, AVG cost 6.20 us / test.
OBJ2 (154799854E0) run 100000 Events, total costs 543.00 ms, AVG cost 5.43 us /test.
OBJ1 (154799851A0) run 100000 Events, total costs 547.00 ms, AVG cost 5.47 us /test.
Test Direct Call...
OBJ1 (154799851A0) run 100000 Direct Calls, total costs 527.00 ms, AVG cost 5.27 us / test.
Finished.

5. 结果分析

可以看见,

  • Release下,Event 和 直接调用的性能几乎完全一样。Debug下略慢。
  • Release下,如果发射的信号只对应一个槽,则比直接调用的性能稍微差一些,但远没有10倍的差距,比如Obj2发射的某个信号,只连接到Obj1 的1个槽,其实区别不大。
  • Release下,如果发射的信号对应多个槽,则性能显著下降,正如obj1发射的一个信号,连接到多个槽,结果就很耗时。发射的信号会给每个槽都走一遭。
  • 如果存在大量的new\delete,则无论何方式,区别都不大。new/delete非常耗时。

6. 开发建议

对于密集的点对点异步调用,显然是Event比较好。如果是广播性质的多对多,Event需要循环,则使用信号与槽会利于开发。此外信号与槽会自动维护双方的可用性,在目的析构后不再调用槽。Events因为要给入指针,则必须自己确保指针的有效性。

提高速度的关键还是使用静态内存,避免频繁new、delete。

7. 改进效果

taskBus的异步调用最后一步队列操作,使用Event而不是信号,来通知线程干活,只获得了大概10ms的延迟优化,是因为大部分延迟实际是在缓存、滤波器上,并不在信号-槽中。使用了QEvent后,构造的全通无线网络,在 128kbps的带宽下,还是要稍微顺畅一些。

Gif

相关文章:

Qt Signals Slots VS QEvents - Qt跨线程异步操作性能测试与选取建议

相关代码参考&#xff1a;https://gitcode.net/coloreaglestdio/qtcpp_demo/-/tree/master/qt_event_signal 1.问题的由来 在对 taskBus 进行低延迟改造时&#xff0c;避免滥用信号与槽起到了较好的作用。笔者在前一篇文章中&#xff0c;叙述了通过避免广播式地播发信号&…...

Postgres 和 MySQL 应该怎么选?

PostgreSQL和MySQL是两个流行的关系型数据库管理系统&#xff08;DBMS&#xff09;。它们都具有一些相似的功能&#xff0c;但也有一些区别。 在选择使用哪个DBMS时&#xff0c;需要考虑多个因素&#xff0c;包括性能、可扩展性、安全性、功能丰富度、生态系统支持等。下面是对…...

【在英伟达nvidia的jetson-orin-nx和PC电脑ubuntu20.04上-装配ESP32开发调试环境-基础测试】

【在英伟达nvidia的jetson-orin-nx和PC电脑ubuntu20.04上-装配ESP32开发调试环境-基础测试】 1、概述2、实验环境3、 物品说明4、参考资料与自我总结5、实验过程1、创建目录2、克隆下载文件3、 拉取子目录安装和交叉编译工具链等其他工具4、添加环境变量6、将样例文件拷贝到桌面…...

我终于搞明白了HTTPS协议了!超长文章!

HTTPS协议是现代互联网中非常重要的一种安全协议&#xff0c;它能够在客户端和服务器之间建立一条安全的通信渠道&#xff0c;确保用户的隐私和数据安全。下面我来详细介绍HTTPS协议的相关知识。 HTTP协议的缺点 HTTP协议是互联网中的一种应用层协议&#xff0c;它负责客户端…...

Golang Testify介绍

简介 Golang是一种编译型语言&#xff0c;由Google开发&#xff0c;已经成为了Web开发领域中非常受欢迎的语言之一。在Golang生态系统中&#xff0c;有许多用于编写测试的框架和库&#xff0c;其中Testify是其中一个非常流行的测试框架。 Testify是一个用于编写测试的扩展包&…...

DALL·E 3怎么用?DALL·E 3如何申请开通 ?DALL·E 3如何免费使用?AI绘画教程来喽~

一、引言 DALLE 3 是 OpenAI 在上个月&#xff08;2023 年 9 月&#xff09;发布的一个文生图模型。 相对于 Midjourney 以及 Stable Diffusion&#xff0c;DALLE 3 最大的便利之处在于&#xff0c;用户不需要掌握 Prompt 的写法了&#xff0c;直接自然语言描述即可。 甚至还…...

安装 Dispatch 库

首先&#xff0c;我们需要安装 Dispatch 库。在命令行中运行以下命令来安装 Dispatch&#xff1a; $ sbt console然后&#xff0c;在 Scala 控制台中&#xff0c;导入所需的库&#xff1a; import dispatch._接下来&#xff0c;我们需要设置代理服务器。在 Dispatch 中&#…...

【Unity程序技巧】异步保险箱管理器

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…...

ChatGPT 助力英文论文翻译和润色

文章目录 一、前言二、主要内容1. 中英互译2. 中文润色3. 英文润色 三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 随着全球化的推进&#xff0c;跨文化交流变得越来越重要。在学术领域&#xff0c;英文论文的质量对于研究成果的传…...

【2024秋招】腾讯云智武汉后端开发一面 2023-9-20

1 java 1.1 hashMap 1.2 哈希冲突的解决方法 1.3 讲解一下CAS的aba问题 1.4 concurrentHashMap的并发方案为什么要使用cas ConcurrentHashMap 是 Java 并发包 java.util.concurrent 中的一个重要组件&#xff0c;用于提供高并发、高性能、线程安全的哈希映射。为了达到这样…...

k8s-----16、配置管理-ConfigMap

ConfigMap 1、作用2、以volume形式进行挂载2.1 创建配置文件2.2 创建ConfigMap文件2.3 最终的yaml文件 3、以变量形式进行挂载3.1 创建configmap文件3.2 书写最终yaml文件 1、作用 存储不加密的数据到etcd中&#xff0c;以变量或者volume形式挂载到pod的容器中场景&#xff1a…...

QML QTP0001 not set 警告

使用QML的时候发现有这个警告。查阅资料之后发现解决办法。 大概的意思是说现在:/qt/qml/ 这个前缀是QML模块资源文件的前缀&#xff0c;而之前是:/ 这是从QT6.5开始的&#xff0c;旧的前缀被标记为废弃的。文档还说在使用qt_add_qml_module()不指定RESOURCE_PREFIX是新版的前…...

Mac M1编译 swift 5.8.1源码

参考链接&#xff1a;https://github.com/apple/swift/blob/main/docs/HowToGuides/GettingStarted.md#system-requirements 编译 Swift 5.8 源码-六虎 解决M1芯片的Homebrew安装问题--For M1使用者_m1 homebrew安装_a_52hz的博客-CSDN博客 建议全程梯子 一、检查和配置环境…...

[极客大挑战 2019]EasySQL

【解题思路】 1.打开靶机链接 2.输入数据进行尝试 输入1,1&#xff1a; 可以在导航栏里面看到username和password的变量。 3.使用万能密码 username&#xff1a;1 or 11# username&#xff1a;任意数据 password&#xff1a;任意数据 …...

统信UOS技术开放日:四大领域全面接入AI大模型能力

1024是程序员的节日&#xff0c;10月24日&#xff0c;统信举办2023统信UOS技术开放日暨deepin Meetup北京站活动&#xff0c;发布与大模型同行的UOS AI、浏览器AI助手、邮箱AI助手、自然语言全局搜索、畅写在线等多项最新AI技术与产品应用。 统信软件高级副总经理、CTO、深度社…...

【Linux系统编程:信号】产生信号 | 阻塞信号 | 处理信号 | 可重入函数

写在前面 通过学习信号可以理解进程与进程的一个相对关系&#xff0c;还能理解操作系统与进程的关系。要注意的是进程间通信中的信号量与这里的信号没有半毛钱关系&#xff0c;就像老婆和老婆饼。 本文要点&#xff1a; 掌握 Linux 信号的基本概念掌握信号产生的一般方式理解…...

Linux NFS的整体架构与核心代码解析

前面文章我们从应用层面对NFS进行了介绍&#xff0c;接下来的文章我们将进入实现层面。本文首先从整体上对Linux的NFS软件架构进行介绍&#xff0c;然后介绍代码与实际业务逻辑介绍一下NFS的处理流程。 NFS文件系统的架构分析 NFS分布式文件系统是一个客户端-服务端架构&#…...

28、Flink 的SQL之DROP 、ALTER 、INSERT 、ANALYZE 语句

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…...

正则表达式[总结]

文章目录 1. 为什么要学习正则表达式2. 再提出几个问题&#xff1f;3. 解决之道-正则表达式4. 正则表达式基本介绍5. 正则表达式底层实现(重要)6. 正则表达式语法6.1 基本介绍6.2 元字符(Metacharacter)-转义号 \\\6.3 元字符-字符匹配符6.4 元字符-选择匹配符6.5 元字符-限定符…...

【docker】搭建xxl-job

首先创建数据库&#xff0c;例如我已经有了mysql 在 192.168.20.17上 #首先要有对应的数据库&#xff0c;创建xxl-job所需表CREATE database if NOT EXISTS xxl_job default character set utf8mb4 collate utf8mb4_unicode_ci; use xxl_job;SET NAMES utf8mb4;CREATE TABLE xx…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...