Qt插件开发总结7--插件通信升级【多线程通信】

embedded/2024/12/22 20:36:20/

一、前言

在本系列文章:Qt插件开发总结3–插件间通信 一文中详细讲解了,插件-插件通信、插件-主程序通信。

但是这样虽然实现了通信,但是存在一定的弊端,之前的通信结构如下所示:

在这里插入图片描述
插件和主程序都必须依靠【插件管理器】作为中转站,来相互通信。如果一旦通信比较密集,那么插件管理器肯定负载跟不上,那通信的效率就会变得很慢,可以理解这是半双工通信,即同一时刻,只能有一个人在收或发。

为此,我们必须改变现有结构,来实现更高效率的通信。


二、方案

之前的通信转发都是在插件管理器的槽函数中,通信体量变大,消息肯定会在这堵塞。

所以我引入线程池,将消息转发功能,打包为线程池任务。

不熟悉线程池的朋友,可以查看我的这篇博客:Qt线程池

消息一到槽函数中,槽函数不做任何处理,直接将其打包并丢入线程池中,线程池自动分配线程来执行任务。这样就变成多窗口同步作业。

在这里插入图片描述
如上图所示,插件或主程序只需要释放信号,信号响应插件管理器的槽函数,槽函数打包线程池任务,线程池子线程执行任务,完成消息转发。

在这里插入图片描述
在这里插入图片描述


三、效果展示

在这里插入图片描述


四、关键代码

4.1、主程序

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QDebug>
#include "PluginManager.h"
#include <QDir>
#include <QMenuBar>
#include <QToolBar>
#include <QTabWidget>
#include <QWidget>
#include <QHBoxLayout>
#include <QLabel>#include "./Widget/speeddashbroad.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void Init_UI();void recvMsgFromManager(PluginMetaData metaData);public:QMenuBar* menuBar;QMenu* menuFile;QMenu* menuEdit;QMenu* menuView;QMenu* menuTool;QTabWidget* tabWidget;public:SpeedDashBroad* sdb;signals:void sendMsgToManager(PluginMetaData);private slots:void slot_PluginAction_MenuBar(QString menu,QStringList actionList);    //添加插件菜单栏Actionvoid slot_PluginsAction_trigger(bool isChecked);    //响应插件Actionvoid slot_PluginWidget(QPluginLoader*,QString);    //添加插件widgetprivate:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);Init_UI();//传递主程序指针给插件管理器PluginManager::instance()->m_mainWin = this;//绑定主程序和插件管理器消息信号槽connect(this,&MainWindow::sendMsgToManager,PluginManager::instance(),&PluginManager::recMsgFromPlugin);//嗅探到的所有插件qDebug()<<"嗅探插件: "<<PluginManager::instance()->CK_allPluginsName().keys();//加载所有插件foreach(QString pluginName , PluginManager::instance()->CK_allPluginsName().keys()) {PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value(pluginName));}//加载所有插件
//    PluginManager::instance()->loadAllPlugins();//加载存在依赖稍候处理的插件while(!PluginManager::instance()->m_remainPlugin.isEmpty()) {QString plugin_filePath = PluginManager::instance()->m_remainPlugin.pop();//qDebug()<<endl<<endl<<"加载存在依赖稍候处理的插件: "<<plugin_filePath;PluginManager::instance()->loadPlugin(plugin_filePath);}//加载其中某个插件
//    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginA"));
//    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginB"));
//    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginC"));
//    PluginManager::instance()->loadPlugin(PluginManager::instance()->CK_allPluginsName().value("pluginD"));//    QPluginLoader *loader1 = PluginManager::instance()->getPlugin("pluginA");
//    PluginInterface* pluginInterface1 = qobject_cast<PluginInterface *>(loader1->instance());
//    pluginInterface1->showSomeThing("新增接口API");//    QPluginLoader *loader2 = PluginManager::instance()->getPlugin("pluginB");
//    PluginInterface* pluginInterface2 = qobject_cast<PluginInterface *>(loader2->instance());
//    pluginInterface2->showSomeThing("新增接口API");//通信测试//================================================================================QPluginLoader *loader1 = PluginManager::instance()->getPlugin("pluginA");if(loader1) {PluginInterface *pluginA = dynamic_cast<PluginInterface*>(loader1->instance());if(pluginA) {PluginMetaData m;m.dest = "pluginB";m.from = "pluginA";m.msg = "插件A发给插件B的消息";pluginA->sendMsgToManager(m);}}QPluginLoader *loader2 = PluginManager::instance()->getPlugin("pluginB");if(loader2) {PluginInterface *pluginB = dynamic_cast<PluginInterface*>(loader2->instance());if(pluginB) {PluginMetaData m;m.dest = "pluginA";m.from = "pluginB";m.msg = "插件B发给插件A的消息";pluginB->sendMsgToManager(m);}}//------------------------------if(loader2) {PluginInterface *pluginB = dynamic_cast<PluginInterface*>(loader2->instance());if(pluginB) {PluginMetaData m1;m1.dest = "mainWin";m1.from = "pluginB";m1.msg = "插件B发给主程序的消息";pluginB->sendMsgToManager(m1);}}//------------------------------PluginMetaData m2;m2.dest = "pluginA";m2.from = "mainWin";m2.msg = "主程序发给插件A的消息";this->sendMsgToManager(m2);}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::Init_UI()
{QWidget* p = takeCentralWidget();   //删除中央窗体if (p) {delete p;}setDockNestingEnabled(true);        //允许嵌套dock//-------------------------------------------------menuBar = new QMenuBar(this);menuFile = new QMenu("文件", this);menuBar->addMenu(menuFile);this->setMenuBar(menuBar);menuEdit = new QMenu("编辑", this);menuBar->addMenu(menuEdit);this->setMenuBar(menuBar);menuView = new QMenu("视图", this);menuBar->addMenu(menuView);this->setMenuBar(menuBar);menuTool = new QMenu("工具", this);menuBar->addMenu(menuTool);this->setMenuBar(menuBar);connect(PluginManager::instance(),&PluginManager::sig_actions,this,&MainWindow::slot_PluginAction_MenuBar);//-------------------------------------------------tabWidget = new QTabWidget(this);tabWidget->setMinimumSize(1000, 800);   // 设置最小宽高setCentralWidget(tabWidget);            // 指定为中心窗口connect(PluginManager::instance(),&PluginManager::sig_widget,this,&MainWindow::slot_PluginWidget);//-------------------------------------------------sdb = new SpeedDashBroad;tabWidget->addTab(sdb,"表盘");
}void MainWindow::slot_PluginAction_MenuBar(QString menu,QStringList actionList)
{QAction * action = nullptr;if(menu == QString::fromLocal8Bit("menuFile")) {for(int i=0; i<actionList.size(); ++i) {action = new QAction(QIcon(), actionList.at(i), this);menuFile->addAction(action);connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);}}if(menu == QString::fromLocal8Bit("menuEdit")) {for(int i=0; i<actionList.size(); ++i) {action = new QAction(QIcon(), actionList.at(i), this);menuEdit->addAction(action);connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);}}if(menu == QString::fromLocal8Bit("menuView")) {for(int i=0; i<actionList.size(); ++i) {action = new QAction(QIcon(), actionList.at(i), this);menuView->addAction(action);connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);}}if(menu == QString::fromLocal8Bit("menuTool")) {for(int i=0; i<actionList.size(); ++i) {action = new QAction(QIcon(), actionList.at(i), this);menuTool->addAction(action);connect(action,&QAction::triggered,this,&MainWindow::slot_PluginsAction_trigger);}}
}void MainWindow::recvMsgFromManager(PluginMetaData metaData)
{qDebug()<<"主程序接收到消息:"<<metaData.msg;
}void MainWindow::slot_PluginsAction_trigger(bool isChecked)
{QAction* action = qobject_cast<QAction*>(sender());for(int i=0; i<PluginManager::instance()->_actionMap.size(); ++i) { //遍历插件管理器action映射表if(PluginManager::instance()->_actionMap.at(i).action == action->text()) {  //映射表中匹配到Action对应的方法PluginInterface* plugin = qobject_cast<PluginInterface *>(PluginManager::instance()->_actionMap.at(i).plugin->instance());  //获取该action对应的接口指针if(plugin) {for(int j=0; j<plugin->_actionName.size(); ++j) {   //遍历该接口指针内的action名字if(plugin->_actionName[j] == action->text()) {plugin->_actionFunction[j](true);break;}}}break;}}
}void MainWindow::slot_PluginWidget(QPluginLoader* loader,QString widget)
{if(widget == QString::fromLocal8Bit("tabWidget")) {PluginInterface* pluginInterface = qobject_cast<PluginInterface *>(loader->instance());QString pluginName = pluginInterface->_Plugin_Name;QWidget* widget = pluginInterface->_widget;if(widget) {tabWidget->addTab(widget,pluginName);}}//也可以预留布局接入点,插件UI嵌入,看自己需求if(widget == QString::fromLocal8Bit("xxxLayout")) {}}

4.2、插件管理器

#ifndef PLUGINMANAGER_H
#define PLUGINMANAGER_H#include "../Plugin_Interface/PluginInterface.h"
#include <QObject>
#include <QPluginLoader>
#include <QVariant>
#include <QAction>
#include <QStack>#include <QThread>
#include <QThreadPool>class MainWindow;
class TransMessageRunable;typedef struct manager_action_map
{QString action;QPluginLoader* plugin;
}MANAGER_ACTION_MAP;class PluginManager : public QObject
{Q_OBJECT
public:explicit PluginManager(QObject *parent = nullptr);~PluginManager();static PluginManager *instance(){if(m_instance==nullptr)m_instance=new PluginManager();return m_instance;}public:MainWindow* m_mainWin;  //主程序指针QStack<QString> m_remainPlugin; //存在依赖待处理插件栈public:QList<MANAGER_ACTION_MAP> _actionMap;void deal_metaData(QPluginLoader* loader,QJsonObject& json);public://扫描JSON文件中的插件元数据void scanMetaData(const QString &filepath,QJsonObject& json);//加载所有插件void loadAllPlugins();//加载其中某个插件void loadPlugin(const QString &filepath);//卸载所有插件void unloadAllPlugins();//卸载某个插件void unloadPlugin(const QString &filepath);//获取所有插件名称QList<QVariant> allPluginsName();//获取所有插件QList<QPluginLoader *> allPlugins();//获取某个插件名称QVariant getPluginName(QPluginLoader *loader);//根据名称获得插件QPluginLoader* getPlugin(const QString &name);//获取库中所有插件名称QHash<QString,QString> CK_allPluginsName();signals:void sig_actions(QString,QStringList);void sig_widget(QPluginLoader*,QString);public slots:void recMsgFromPlugin(PluginMetaData);void slot_test();private:static PluginManager *m_instance;class PluginsManagerPrivate;PluginsManagerPrivate *managerPrivate;TransMessageRunable* m_transMessageRunable;};#endif // PLUGINMANAGER_H
#include "PluginManager.h"
#include "mainwindow.h"
#include "TransMessageRunable.h"#include <QDir>
#include <QCoreApplication>
#include <QJsonArray>
#include <QDebug>PluginManager* PluginManager::m_instance=nullptr;class PluginManager::PluginsManagerPrivate
{
public:PluginsManagerPrivate(){m_names.clear();m_versions.clear();m_dependencies.clear();m_loaders.clear();m_dependencies_temp.clear();}~PluginsManagerPrivate(){}QHash<QString, QVariant> m_names;               //插件路径--插件名称QHash<QString, QVariant> m_versions;            //插件路径--插件版本QHash<QString, QVariantList> m_dependencies;    //插件路径--插件额外依赖的其他插件QHash<QString, QPluginLoader *> m_loaders;      //插件路径--QPluginLoader实例QHash<QString, QVariantList> m_dependencies_temp;bool check(const QString &filepath)             //插件依赖检测{//qDebug()<<QString("=============== bool check(%1) ===============").arg(filepath);bool status = true;foreach (QVariant item, m_dependencies_temp.value(filepath)) {QString dependencyPluginInfo = item.toString();// 依赖的插件名称、版本、路径QStringList List = dependencyPluginInfo.split(':');QString name_str = List[0];QString version_str = List[1];QString path = m_names.key(name_str);//            qDebug()<<"=== 插件依赖信息 ===";
//            qDebug()<<"name_str: "<<name_str;
//            qDebug()<<"version_str: "<<version_str;
//            qDebug()<<"path: "<<path;QVariant name = QVariant(name_str);QVariant version = QVariant(version_str);/********** 检测插件是否依赖于其他插件 **********/// 先检测插件名称if (!m_names.values().contains(name)) {//qDebug() << "=== 插件" << filepath <<"  缺少依赖插件:" << name.toString();status = false;continue;}// 再检测插件版本if (m_versions.value(path) != version) {//qDebug() << "=== 依赖插件: " << name.toString() << "当前版本为: " << m_versions.value(m_names.key(name)).toString() << "但是需要依赖插件版本为: " << version.toString();status = false;continue;}// 然后,检测被依赖的插件是否还依赖于另外的插件if (!check(path)) {//qDebug() << "=== 依赖插件:" << name.toString() << "又依赖: " << path;status = false;continue;}}//qDebug()<<"status: "<<status;return status;}};PluginManager::PluginManager(QObject *parent) : QObject(parent)
{managerPrivate = new PluginsManagerPrivate;//线程池//========================================================================//设置线程池最大线程数量QThreadPool::globalInstance()->setMaxThreadCount(10);}
PluginManager::~PluginManager()
{delete managerPrivate;
}void PluginManager::loadAllPlugins()
{QDir pluginsDir(qApp->applicationDirPath());    //pluginsDir: "../build-xxx-debug/debug"if(pluginsDir.dirName().toLower() == "debug" ||pluginsDir.dirName().toLower() == "release") {pluginsDir.cdUp();  //pluginsDir: "../build-xxx-debug"pluginsDir.cdUp();  //pluginsDir: "../"}pluginsDir.cd("plugins");QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);//加载插件for(QFileInfo fileinfo : pluginsInfo)loadPlugin(fileinfo.absoluteFilePath());
}void PluginManager::scanMetaData(const QString &filepath,QJsonObject& json)
{//判断是否为库(后缀有效性)if(!QLibrary::isLibrary(filepath))return;if(managerPrivate->m_names.keys().contains(filepath)) {//qDebug()<<QString("插件: %1 已加载,退出!!!").arg(filepath);return;}//获取元数据QPluginLoader *loader = new QPluginLoader(filepath);//qDebug()<<loader->metaData().keys();json = loader->metaData().value("MetaData").toObject();
//    for(int i=0; i<json.keys().size(); ++i) {
//        qDebug()<<json.keys().at(i)<< " : "<<json.value(json.keys().at(i));
//    }managerPrivate->m_names.insert(filepath, json.value("name").toVariant());managerPrivate->m_versions.insert(filepath, json.value("version").toVariant());managerPrivate->m_dependencies.insert(filepath, json.value("dependencies").toArray().toVariantList());//qDebug()<<"dependencies: "<<json.value("dependencies").toArray().toVariantList();//qDebug()<<"managerPrivate->m_dependencies: "<<managerPrivate->m_dependencies.values();delete loader;loader = nullptr;
}void PluginManager::loadPlugin(const QString &filepath)
{//库文件类型检测if(!QLibrary::isLibrary(filepath))return;//读取当前插件依赖//qDebug()<<"===========================================================================";QPluginLoader *loader_temp = new QPluginLoader(filepath);QJsonObject json_temp = loader_temp->metaData().value("MetaData").toObject();managerPrivate->m_dependencies_temp.insert(filepath, json_temp.value("dependencies").toArray().toVariantList());//qDebug()<<"managerPrivate->m_dependencies_temp: "<<managerPrivate->m_dependencies_temp;delete loader_temp;loader_temp = nullptr;//qDebug()<<"@@@ 当前加载插件: "<<filepath;//检测依赖if(!managerPrivate->check(filepath)) {//qDebug()<<"当前插件存在依赖,入栈,稍候处理: "<<filepath;m_remainPlugin.push(filepath);//清空当前插件依赖managerPrivate->m_dependencies_temp.clear();return;}//清空当前插件依赖managerPrivate->m_dependencies_temp.clear();//检测当前插件是否已加载if(managerPrivate->m_loaders.keys().contains(filepath)) {//qDebug()<<"当前插件已加载!!!";return;}//加载插件QPluginLoader *loader = new QPluginLoader(filepath);if(loader->load()) {PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());if(plugin) {//检测元信息QJsonObject json;scanMetaData(filepath,json);deal_metaData(loader,json);managerPrivate->m_loaders.insert(filepath, loader);connect(loader->instance(),SIGNAL(sendMsgToManager(PluginMetaData)),this,SLOT(recMsgFromPlugin(PluginMetaData)));//绑定if(plugin->_Plugin_Name == QString::fromLocal8Bit("PluginA")) {//qDebug()<<"plugin->_Plugin_Name: "<<plugin->_Plugin_Name;connect(loader->instance(),SIGNAL(sig_test()),this,SLOT(slot_test()));}plugin->Info(QString(" %1 加载成功!").arg(plugin->_Plugin_Name));}else {delete loader;loader = nullptr;}}else{qDebug()<<"loadPlugin:"<<filepath<<loader->errorString();}
}void PluginManager::unloadAllPlugins()
{for(QString filepath : managerPrivate->m_loaders.keys())unloadPlugin(filepath);
}void PluginManager::unloadPlugin(const QString &filepath)
{if(!managerPrivate->m_loaders.keys().contains(filepath)) {return;}QPluginLoader *loader = managerPrivate->m_loaders.value(filepath);//卸载插件,并从内部数据结构中移除if(loader->unload()) {PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());if(plugin) {plugin->Info("插件卸载成功!");managerPrivate->m_loaders.remove(filepath);delete loader;loader = nullptr;}}
}QList<QPluginLoader *> PluginManager::allPlugins()
{return managerPrivate->m_loaders.values();
}QList<QVariant> PluginManager::allPluginsName()
{return managerPrivate->m_names.values();
}QVariant PluginManager::getPluginName(QPluginLoader *loader)
{if(loader)return managerPrivate->m_names.value(managerPrivate->m_loaders.key(loader));elsereturn "";
}QPluginLoader *PluginManager::getPlugin(const QString &name)
{return managerPrivate->m_loaders.value(managerPrivate->m_names.key(name));
}void PluginManager::recMsgFromPlugin(PluginMetaData metaData)
{TransMessageRunable* task = new TransMessageRunable(metaData);task->setAutoDelete(true);  //autoDelete属性默认为true   QThreadPool会在run()函数运行结束后,自动删除了MyTask对象QThreadPool::globalInstance()->start(task); //任务放进线程池//QThread::sleep(1);//QThreadPool::globalInstance()->waitForDone(); //等待任务结束
}void PluginManager::slot_test()
{//qDebug()<<"触发槽函数: PluginManager::slot_test()";
}QHash<QString,QString> PluginManager::CK_allPluginsName()
{QDir pluginsDir(qApp->applicationDirPath());    //pluginsDir: "../build-xxx-debug/debug"if(pluginsDir.dirName().toLower() == "debug" ||pluginsDir.dirName().toLower() == "release") {pluginsDir.cdUp();  //pluginsDir: "../build-xxx-debug"pluginsDir.cdUp();  //pluginsDir: "../"}pluginsDir.cd("plugins");QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);//库中插件QHash<QString,QString> pluginNames;for(QFileInfo fileinfo : pluginsInfo){if(fileinfo.fileName().contains(".dll")) {QString pluginName = fileinfo.fileName().mid(0,fileinfo.fileName().size()-4);QString pluginPath = fileinfo.filePath();pluginNames.insert(pluginName,pluginPath);}}return pluginNames;
}void PluginManager::deal_metaData(QPluginLoader* loader,QJsonObject& json)
{QString name;if(json.keys().contains("name")) {QJsonValue JValue = json.value("name").toString();name = JValue.toString();}//------------------------------------------------------------QString menu;QStringList actionList;if(json.keys().contains("menu")) {QJsonValue JValue = json.value("menu").toString();menu = JValue.toString();}if(json.keys().contains("action")) {QJsonArray JArray = json.value("action").toArray();for(int i=0;i<JArray.size();++i) {actionList << JArray.at(i).toString();MANAGER_ACTION_MAP manager_action_map;manager_action_map.action = JArray.at(i).toString();manager_action_map.plugin = loader;_actionMap.push_back(manager_action_map);}}QStringList dependencies_List;if(json.keys().contains("dependencies")) {QJsonArray JArray = json.value("dependencies").toArray();for(int i=0;i<JArray.size();++i) {dependencies_List << JArray.at(i).toString();}}//qDebug()<<"dependencies_List: "<<dependencies_List;//------------------------------------------------------------if(!menu.isEmpty() && !actionList.empty()) {emit sig_actions(menu,actionList);}//------------------------------------------------------------//------------------------------------------------------------QString widget;if(json.keys().contains("widget")) {QJsonValue JValue = json.value("widget").toString();widget = JValue.toString();if(!widget.isEmpty()) {sig_widget(loader,widget);  //发送:插件对象、主界面预留接入点}}//------------------------------------------------------------
}

4.3、线程池任务类

#ifndef TRANSMESSAGERUNABLE_H
#define TRANSMESSAGERUNABLE_H#include <QObject>
#include <QRunnable>
#include <QDebug>#include "PluginManager.h"
#include "mainwindow.h"class TransMessageRunable : public QObject, public QRunnable
{Q_OBJECT
public:explicit TransMessageRunable(PluginMetaData metaData, QObject *parent = nullptr);~TransMessageRunable();PluginMetaData m_metaData;protected:void run();};#endif // TRANSMESSAGERUNABLE_H
#include "TransMessageRunable.h"TransMessageRunable::TransMessageRunable(PluginMetaData metaData, QObject *parent) : QObject(parent)
{m_metaData = metaData;
}TransMessageRunable::~TransMessageRunable()
{//qDebug()<<"delete Task";
}void TransMessageRunable::run()
{//和主程序通信//------------------------------------------------------------if(m_metaData.dest == QString::fromLocal8Bit("mainWin")) {if(PluginManager::instance()->m_mainWin) {PluginManager::instance()->m_mainWin->recvMsgFromManager(m_metaData);}return;}//和插件通信//------------------------------------------------------------auto loader = PluginManager::instance()->getPlugin(m_metaData.dest); //目标插件if(loader) {auto interface = qobject_cast<PluginInterface*>(loader->instance());if(interface) {interface->recMsgFromManager(m_metaData); //转发给对应的插件}}
}

http://www.ppmy.cn/embedded/103352.html

相关文章

常见协议工作原理 https ARP ICMP DHCP PING

1. HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09; HTTPS是HTTP的安全版本&#xff0c;它在HTTP和TCP之间加入了SSL/TLS协议层&#xff0c;用于加密数据传输&#xff0c;确保数据的安全性和完整性。 工作原理&#xff1a; 握手&#xff1a;客户端和服务器…

【C++ Primer Plus习题】7.7

问题: 解答: #include <iostream> using namespace std;#define SIZE 10double* fill_array(double* begin, double* end) {for (begin; begin < end; begin){cout << "请输入值:";cin >> *begin;if (cin.fail()){cout << "非法数字…

进程间的通信

建立两个.c 建立子父进程&#xff0c;父进程发送消息到队列&#xff0c;子进程读取队列&#xff0c;另一个同样。 #include <myhead.h> struct msga {long mtype;char mtext[1024]; }; #define len sizeof(struct msga)-sizeof(long) int main(int argc, const char *arg…

缩短单片机内的Flash的擦写时间

查看单片机&#xff08;HC32F448&#xff09;的数据手册得到块擦除时间为184Thclk &#xff08;ms&#xff09;。 一般数据存储都是将1改为0的情况&#xff0c;无法从0改为1&#xff0c;所以需要先擦除然后存储数值。如何缩短Flash的擦写时间&#xff1f; 提高时钟频率。 软件…

Open3D mesh 网格简化(顶点聚类)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始mesh 3.2聚类后的mesh Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&#xff0…

LIN诊断(4)——Tp层函数LINtp.dll应用

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

SQL视图:简化复杂查询的利器

SQL视图&#xff1a;简化复杂查询的利器 在数据库管理系统中&#xff0c;视图&#xff08;View&#xff09;是一种虚拟表&#xff0c;其内容由SQL查询定义。视图可以简化复杂的查询&#xff0c;提高数据的安全性&#xff0c;并使得数据的展示更加直观。本文将详细介绍如何使用…

【使用python实现多目标批量ping】附案例

以下为使用 Python 实现批量 ping 的多种方法及代码示例&#xff1a; 方法一&#xff1a; import subprocessfilepath E:\\Python\\tools\\AutoMatic\\hosts.txt with open(filepath, r) as f:hosts f.readlines()for host in hosts:result subprocess.check_output((ping…