[C++ QT项目实战]----C++ QT系统实现多线程通信

news2024/10/6 10:32:36

前言

        在C++ QT中,多线程通信原理主要涉及到信号与槽机制和事件循环机制。

        1、信号与槽机制:
        在QT中,信号与槽是一种用于对象间通信的机制。对象可以通过发送信号来通知其他对象,其他对象通过连接槽来接收信号并进行相应的处理。这个机制可以跨线程使用,即一个线程可以发送信号,另一个线程可以接收信号。

        在使用多线程时,通过将一个对象的信号与另一个对象的槽连接起来,可以在不同线程中进行通信。当信号发出时,QT会在接收对象所在的线程中调用槽函数,从而实现多线程之间的通信。

        2、事件循环机制:
        在QT中,每个线程都有自己的事件循环。事件循环是处理事件的机制,包括用户输入、定时器事件等。当一个线程启动时,会进入事件循环,等待事件发生并处理事件。

        在多线程通信中,通常会涉及到在不同线程之间传递事件。通过在一个线程中发出事件,另一个线程中的事件循环可以接收并处理这个事件,从而实现线程间的通信。

        总的来说,C++ QT中的多线程通信原理是通过信号与槽机制和事件循环机制来实现的。通过使用这两种机制,可以在不同线程之间进行通信,并实现多线程之间的协作。但需要注意线程安全性,避免出现竞态条件和线程安全问题。

正文

01-功能演示

        如下图所示,第一张图,是经过运行之后,进入主界面,会同时显示后台信息,黑色的控制台界面就是线程运行信息,是否有两个线程也可以查看,主界面可以通过控件控制进程运行状态。        

        注:系统资源竞态是指多个线程或进程在对共享资源进行访问时,由于执行顺序不确定或未正确同步,导致竞争条件发生,从而导致数据不一致或程序出现错误的现象。

        当多个线程同时访问共享资源时,如果没有使用适当的同步机制来确保每个线程对资源的访问顺序,就可能引发系统资源竞态。系统资源竞态会导致一些严重问题,包括但不限于以下几点:

        数据不一致性:如果多个线程同时访问共享数据但没有正确同步,可能会导致数据不一致的情况。例如,一个线程正在修改数据的同时,另一个线程也在读取或修改相同的数据,可能会导致数据错误或不一致。

        资源泄漏:当多个线程同时竞争资源而没有正确释放资源,可能会导致资源泄漏。例如,并发写入文件时,如果某个线程在写入文件后未正确关闭文件句柄,可能会导致文件描述符泄漏。

        死锁:系统资源竞态还可能导致死锁的发生。当两个或多个线程相互等待对方释放资源时,可能导致所有参与的线程无法继续正常执行,从而导致程序挂起。

        因此,在采用系统多线程运行时,一定要使用一些方法防止资源竞争,可以采取以下几种措施:a、使用互斥量(Mutex)或信号量(Semaphore)等同步机制,在访问共享资源时确保只有一个线程可以访问;b、 使用临界区(Critical Section)来限制对共享资源的访问,确保线程之间的同步;c、使用条件变量(Condition Variable)在需要等待某个条件满足时进行线程等待和唤醒。

        这里的系统使用的是数据库进行资源交换和读取等,线程之间互不干扰,不存在线程竞争问题。

         下面是主线程和子线程之间通信的几个案例,也是系统中运行产生的,如下图所示,系统改为手动模式,首先点击打开配置文件按钮,可以看出控制台运行信息为“配置文件config.ini已打开!”,点击平台数据读取按钮,显示下方运行信息,这就是主线程和子线程之间的交互信息,可以通过QT中信号与槽函数的形式实现。

        下面重点分析如何实现,并附代码解释。

02-系统多线程实现

        这里讲解四个实现函数文件,其实是两个文件:mainwindow.h和mainwindow.cpp文件为主线程文件Zanj_WFC_ctrl.h和Zanj_WFC_ctrl.cpp文件为子线程文件,下面一一解释。

        mainwindow.cpp文件

        (1)、该文件用于实现显示主线程中的主界面,也就是运行之后,第一个界面,可以在界面上进行操作,后台信息是由于spdlog日志信息的导入,才会进行信息显示,便于观察,#include "spdlog/spdlog.h",代码中下面这一部分就是建立子线程的核心,QT中可以使用moveToThread()函数建立新线程。具体介绍如下:

        a、m_Zanj_WFC_ctrl = new Zanj_WFC_ctrl;// 这个实例要负责预报计算任务
        b、m_Zanj_WFC_ctrl_Thread = new QThread; // 子线程,本身不负责预报计算
        c、connectZanj_WFC_ctrl(m_Zanj_WFC_ctrl); // 连接信号-槽,复制的开始和取消指令是通过信号发送的
        d、m_Zanj_WFC_ctrl->moveToThread(m_Zanj_WFC_ctrl_Thread);// 将实例移动到新的线程,实现多线程运行
        e、m_Zanj_WFC_ctrl_Thread->start();

        (2)、函数connectZanj_WFC_ctrl()用于连接子线程函数,里面有该函数实现的功能,使用connect()函数编写代码: connect(this, SIGNAL(system_predict_init_link()), m_Zanj_WFC_ctrl, SLOT(HDGL_SYSTEM_Init()));这个函数HDGL_SYSTEM_Init()在子线程中实现,也就是说只要主线程启动,子线程也会跟着启动,可以在该函数中实现子线程的功能。

        (3)、上述描述的主线程与子线程通信的案例中的实现过程如下:也是通过信号与槽函数方式,比如打开配置文件函数,首先对该函数进行实现on_Open_config_Btn_clicked(),该函数就是打开配置文件控件对应的函数,该函数实现了点击按钮之后,发送了信号emit send_config();该信号通过connect()函数发送到子线程,代码为connect(this, &MainWindow::send_config, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::OnconfigSlot);OnconfigSlot()函数在子线程中实现。

        原理就是这样,虽有些繁琐,但是逻辑性强。另一个读取数据按钮原理一样。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "Zanj_WFC_ctrl.h"
#include "qdebug.h"
#include <qstring.h>

#include "cglobal.h"
#include <QElapsedTimer>
#include <iostream>
#include "windows.h"
#pragma comment(lib,".\\lib\\fmtd.lib")

#include "spdlog/fmt/ostr.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"


#include  <QTimer>
#include  <time.h>
#include  <QLabel>
#include  <QValueAxis>
#include  <QMargins>
#include  <QtWidgets/QApplication>
#include  <QtWidgets/QMainWindow>

#include <ctime>  

using namespace std::literals;
using namespace std;
namespace spd = spdlog;
auto console_mainWindow = spd::stdout_color_mt("主界面控制台");
auto rotating_logger_mainWindow = spdlog::rotating_logger_mt("CAO8_Winmain_rotating", "logs/CAO8_rotating.logger", 1048576 * 5, 12);

MainWindow::MainWindow(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::MainWindow)
{
	ui->setupUi(this);
	setWindowTitle(QString::fromLocal8Bit("主系统")); // 此处写应用程序在标题栏上显示的名称
	setStyleSheet("background - color:pink; ");
	reshow();
	data_log_tblview();
	console_mainWindow->info("欢迎进入主系统UI线程[Mainwindow]!");
	rotating_logger_mainWindow->info("欢迎进入主系统UI线程[Mainwindow]!");
	 

	m_Zanj_WFC_ctrl = new Zanj_WFC_ctrl;// 这个实例要负责预报计算任务
	m_Zanj_WFC_ctrl_Thread = new QThread; // 子线程,本身不负责预报计算
	connectZanj_WFC_ctrl(m_Zanj_WFC_ctrl); // 连接信号-槽,复制的开始和取消指令是通过信号发送的
	m_Zanj_WFC_ctrl->moveToThread(m_Zanj_WFC_ctrl_Thread);// 将实例移动到新的线程,实现多线程运行
	m_Zanj_WFC_ctrl_Thread->start();

	emit system_predict_init_link();

	
	// 向子线程发送打开配置文件信息
	connect(this, &MainWindow::send_config, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::OnconfigSlot);
	// 向子线程发送读取数据信号,子线程接收后,进行数据读取
	connect(this, &MainWindow::send_rChk_ToWFC, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::OnGETSlot);

}

MainWindow::~MainWindow()
{
	delete ui;
}

// 这里连接子线程,进行子线程的开启,子线程连接数据库,连接大数据平台,打开所需文件的信号传递,以及MainWindow界面槽函数处理信号的实现
void MainWindow::connectZanj_WFC_ctrl(Zanj_WFC_ctrl * m_Zanj_WFC_ctrl)
{
	connect(this, SIGNAL(system_predict_init_link()), m_Zanj_WFC_ctrl, SLOT(HDGL_SYSTEM_Init()));// 使用信号-槽机制,发出开始指令

}

// 打开配置文件函数
void MainWindow::on_Open_config_Btn_clicked()
{
	ui->Iplate4j_IP_QLE->setText("127.0.0.1");
	ui->model_PC_IP_QLE->setText("127.0.0.1");
	emit send_config();
}

// 由主窗口发送数据读取信号,子线程实现数据读取操作
void MainWindow::on_Restful_data_read_Btn_clicked()
{
	emit send_rChk_ToWFC();

}
        mainwindow.h文件

        该文件就是.cpp文件中众多函数的声明,以及信号的声明,比如读取数据时发送的信号需要在这里声明一下,便于使用。Zanj_WFC_ctrl * m_Zanj_WFC_ctrl; // 预报计算的类
    QThread * m_Zanj_WFC_ctrl_Thread; //将被移动到此线程执行
,这里也定义了指针,用于在.cpp文件中使用。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include"ui_mainwindow.h"
#include <QMainWindow>
#include <QLabel>
#include <QtSql/QtSql>

#include "spdlog/fmt/ostr.h"
#include "Zanj_WFC_ctrl.h"
#include <QVariant>
#include "wfc_to_main.h"

#include <QtSql/QtSql>
#include <QDateTime>
#include <QTimer>
#include <qtextcodec.h> 
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>

#include <QMouseEvent>
//一定要声明!!!
#include"dialog_data_log.h"
//#include<vector>

//#include "dialog_data_log.h"
namespace Ui {
	class MainWindow;
}

class MainWindow : public QMainWindow
{
	Q_OBJECT

public:
	explicit MainWindow(QWidget *parent = nullptr);
	~MainWindow();

	Zanj_WFC_ctrl * m_Zanj_WFC_ctrl; // 预报计算的类
	QThread * m_Zanj_WFC_ctrl_Thread; //m_Mp_Pred将被移动到此线程执行
	void connectZanj_WFC_ctrl(Zanj_WFC_ctrl *m_Zanj_WFC_ctrl);

private slots :

	void on_Restful_data_read_Btn_clicked();   //点击平台数据读取按钮,实现读取数据信号发送	
	void on_Open_config_Btn_clicked();


signals:
	
	void system_predict_init_link();//定义向子线程发送的信号,用于实现子线程开启
    void send_config();      // 定义向子线程发送打开配置文件的信号
	void send_rChk_ToWFC();  // 定义向子线程发送数据读取的信号
	
private:
	Ui::MainWindow *ui;

	QLabel * pLabel;

};

#endif // MAINWINDOW_H
        Zanj_WFC_ctrl.cpp文件

        (1)、该文件中的众多函数就是对主线程发送的信号的实现过程,当接受到主线程的信号之后,如果需要对其进行处理,便可以在这里定义函数实现过程,该HDGL_SYSTEM_Init()函数就是对主线程中connectZanj_WFC_ctrl()连接函数的实现。

         (2)、案例中主线程发送读取数据信号,子线程中函数的实现OnGETSlot()在该文件中,这里适用于读取数据函数的实现,可以根据需要进行修改。打开配置文件的实现原理与之一致。

#include "Zanj_WFC_ctrl.h"
#include "wfc_to_main.h"
#include <Python.h>
#include "qdebug.h"
#include <qstring.h>
#include <Python.h>

#pragma comment(lib,"mclmcrrt.lib")  
#pragma comment(lib,"libmx.lib")  
#pragma comment(lib,"libmat.lib")  
#pragma comment(lib,"mclmcr.lib")  
#pragma comment(lib,".\\lib\\CGL_Mech_Property_BP_pred.lib")
#pragma comment(lib,".\\lib\\fmtd.lib")


#include <QAxObject>
#include <QStandardPaths>
#include <QEventLoop>
#include <QDesktopWidget>

#include <algorithm>
#include <numeric>
#include <vector>
#include <QVector>
#include <stdlib.h>

#include "spdlog/fmt/ostr.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"

using namespace libxl;
using namespace std::literals;
//using namespace fmt;
using namespace std;
namespace spd = spdlog;
auto console_WFC_thread = spd::stdout_color_mt("WFC计算线程 ");
auto rlogger_WFC_thread = spdlog::rotating_logger_mt("CAO8_WFC_thread_rotating", "logs/CAO8_rotating.logger", 1048576 * 5, 12);


int Zanj_WFC_ctrl::HDGL_SYSTEM_Init()
{

	rlogger_WFC_thread->flush_on(spd::level::debug);

	console_WFC_thread->info("欢迎进入主系统计算线程");
	str_info = QString::fromLocal8Bit("0 应用程序环境路径  ") + path_deploy;

	return 1;
}

void Zanj_WFC_ctrl::OnGETSlot()
{
	QString strMessage;
	QString strResult;
	QString strUrl;
	QString strInput;
	QFile file(QApplication::applicationDirPath() + "/1.json");
	if (!file.open(QIODevice::WriteOnly)) {
		console_WFC_thread->error("Json file open failed! ");
		rlogger_WFC_thread->error("Json file open failed! ");
	}
	else {
		console_WFC_thread->info("Json file open successfully! ");
		rlogger_WFC_thread->info("Json File open successfully! ");
	}


	//QString strInput = ui.textEdit_input->toPlainText();
	//strUrl += "http://" + ui.lineEdit_Url->text();
	strUrl = serverIP + serviceID_get;
	strInput = clientSecret;
	SendAndGetText(strUrl, get1, strInput, strMessage, strResult);

	QJsonParseError jsonError;
	QByteArray byteResult = strResult.toUtf8();//QString转QByteArray
											   //QJsonDocument json = QJsonDocument::fromJson(byteResult, &jsonError);
	json_doc_get = QJsonDocument::fromJson(byteResult, &jsonError);

	if (json_doc_get.isNull())
	{
		console_WFC_thread->critical("json文件为空文档");
		//console->critical("jsonError错误类型" + jsonError.errorString().toStdString());
		//qDebug() << jsonError.errorString();
		rlogger_WFC_thread->critical("json格式错误[json_doc_get_slot]");
		//qDebug() << "json格式错误";//注意:如果文档中有中文,就会报格式错误
	}
	else if (jsonError.error != QJsonParseError::NoError)
	{
		console_WFC_thread->critical("json格式错误[json_doc_get_slot]" + jsonError.errorString().toStdString());
		//qDebug() << jsonError.errorString();
		rlogger_WFC_thread->critical("json格式错误[json_doc_get_slot]");
	}
	else 
	{
		if (json_doc_get.isObject()) 
		{
			doc_get_Obj = json_doc_get.object();
		}
	}
	json_doc_get_parse();
//	RT_Iplat4j_Communication();  //放在这里可以在主窗口点击数据读取按钮之后,才会提示是否数据平台连接成功

	file.write(json_doc_get.toJson(QJsonDocument::Indented)); //Indented:表示自动添加/n回车符
	file.close();

}

void Zanj_WFC_ctrl::OnconfigSlot()
{
	configuration_read();
}

         Zanj_WFC_ctrl.h文件

        该文件也是.cpp文件中众多函数的声明,比如处理读取数据时发送的信号的函数需要在这里声明一下,便于使用。

#pragma once

#include <QObject>
#include <QtSql/QtSql>
#include <QVariant>

//#include <connection.h>
//#include <QSql>
#include <QSqlError>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>
#include <QMessageBox>
#include <QEventLoop>
#include <qstring.h>
#include <string>
#include "snap7.h"
#include <QtCore/QJsonObject>
#include <QtCore/QJsonDocument>
#include <Python.h>


class Zanj_WFC_ctrl : public QObject
{
	Q_OBJECT

	
public:
	Zanj_WFC_ctrl(QObject *parent=0);
	~Zanj_WFC_ctrl();

public slots:

	int HDGL_SYSTEM_Init();

	void OnGETSlot();
	
	void OnconfigSlot();
	

总结

     同时运行的两个线程,与单线程相比:

        提高系统性能:双线程可以并行执行,可以更充分地利用多核处理器的资源,从而提高系统的整体性能。

        增加程序响应速度:通过将一些耗时的操作放在单独的线程中执行,可以避免阻塞主线程,提高程序的响应速度和用户体验。

        提高系统的并发性:双线程可以同时处理不同的任务,增加系统的并发性,使系统更具有高效性和灵活性。

        分解复杂任务:通过将复杂任务拆分成多个线程来执行,可以更容易地管理和维护代码,提高代码的可读性和可维护性。

        改善软件的稳定性:通过将不同功能模块分别放在不同的线程中执行,可以避免由于一个线程的错误导致整个系统崩溃的情况,提高软件的稳定性。

        总的来说,双线程可以提高系统的性能、响应速度和并发性,同时也能改善软件的稳定性和可维护性,是一种常用的提升程序效率的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1627059.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

与 Apollo 共创生态:Apollo 七周年大会给带来的震撼

文章目录 一、七年蛰伏&#xff0c;Apollo 迎来“智变”时刻二、Apollo 企业生态计划与开放平台2.1 Apollo X 企业自动驾驶解决方案2.2 Apollo 开放平台携手伙伴共创生态 三、个人感悟 一、七年蛰伏&#xff0c;Apollo 迎来“智变”时刻 让我们把时间倒回到 2013 年&#xff0…

OpenHarmony语言基础类库【@ohos.util.LightWeightMap (非线性容器LightWeightMap)】

LightWeightMap可用于存储具有关联关系的key-value键值对集合&#xff0c;存储元素中key值唯一&#xff0c;每个key对应一个value。 LightWeightMap依据泛型定义&#xff0c;采用轻量级结构&#xff0c;初始默认容量大小为8&#xff0c;每次扩容大小为原始容量的两倍。 集合中…

C++中的queue(容器适配器)

目录 一、成员函数 一、构造函数 二、入栈 push 三、出栈 pop 四、判空 empty 五、队列大小 size 六、取队头元素 front 七、取队尾元素 back 八、入栈 emplace 九、交换函数 swap 二、非成员函数重载 一、关系运算符重载 二、交换函数 swap C中的queue不再是容…

pytest-xdist:远程多主机 - 分布式运行自动化测试

简介&#xff1a;pytest-xdist插件使用新的测试执行模式扩展了pytest&#xff0c;最常用的是在多个CPU之间分发测试以加快测试执行&#xff0c;即 pytest -n auto同时也是一个非常优秀的分布式测试插件&#xff0c;分别支持ssh和socket两种方式实现master和worker的远程通讯。…

Java8 Stream常见用法

Stream流的常见用法&#xff1a; 1.利用stream流特性把数组转list集合 //定义一个数组Integer[] array {5,2,1,6,4,3};//通过stream特性把数组转list集合List<Integer> list Arrays.stream(array).collect(Collectors.toList());//打印结果System.out.println(list);…

Ubuntu16.04搭建webrtc服务器

本人查阅无数资料,历时3周搭建成功 一、服务器组成 AppRTC 房间+Web服务器 https://github.com/webrtc/apprtcCollider 信令服务器,在AppRTC源码里CoTurn coturn打洞+中继服务器 Nginx 服务器,用于Web访问代理和Websocket代理。AppRTC 房间+Web服务器使用python+js语言 App…

两大成果发布!“大规模量子云算力集群”和高性能芯片展示中国科技潜力

在当前的科技领域&#xff0c;量子计算的进步正日益引起全球的关注。中国在这一领域的进展尤为显著&#xff0c;今天&#xff0c;北京量子信息科学研究院&#xff08;以下简称北京量子院&#xff09;和中国科学院量子信息与量子科技创新研究院&#xff08;以下简称量子创新院&a…

2024年度延安市农业农村领域科技创新研发平台申报类别程序、相关要求

一、征集类别 此次征集类别包括市级农业科技园区、星创天地、县域科技创新试验示范站、科技示范镇、乡村振兴科技示范村。 二、申报程序 1.农业科技园区由乡(镇)人民政府牵头申报,经县(市、区)科技管理部门审核后向市科技局推荐报送。(申请模板见附件1)。 2.县域科技创新试…

Unreal Engine子类化系统UButton

UE系统Button点击事件无法传递参数&#xff0c;通过子类化系统Button添加自定义参数扩展实现Button点击事件参数传递点击C类文件夹&#xff0c;在右边的区域点击鼠标右键&#xff0c;在弹出的菜单中选择“新建C类”在弹出的菜单中选中“显示所有类”&#xff0c;选择Button作为…

DSP开发实战教程--EPWM模块的影子寄存器详细讲解原理和代码实例

EPWM模块影子寄存器的原理 在TI&#xff08;Texas Instruments&#xff09;的DSP28335中&#xff0c;EPWM&#xff08;Enhanced Pulse Width Modulator&#xff09;模块提供了高精度、高灵活性的PWM信号生成功能。为了能在不影响当前PWM波形输出的情况下预装载新的PWM参数&…

5.Labview簇、变体与类(下) -- 项目与Labview类的使用

本文介绍Labview类的使用&#xff0c;在Labview中&#xff0c;何为类&#xff1f;应该如何理解&#xff1f;具体有什么应用场景&#xff1f; 本文基于Labview软件&#xff0c;讲解了类函数的使用方法和场景&#xff0c;从理论上讲解其数据流的底层概念&#xff0c;从实践上建立…

机器人前馈控制MATLAB实现

在机器人控制中&#xff0c;前馈控制是一种常用的方法&#xff0c;用于补偿系统中的已知动态。前馈控制通常与反馈控制结合使用&#xff0c;以提高系统的跟踪性能和响应速度。在MATLAB中实现机器人前馈控制涉及几个步骤&#xff0c;包括系统建模、设计前馈控制器、实现控制算法…

Docker 入门篇(二)-- Linux 环境离线安装

引言 docker 系列文章&#xff1a; Docker 入门篇&#xff08;一&#xff09;-- 简介与安装教程&#xff08;Windows和Linux&#xff09; 一、安装环境准备 centos &#xff1a;CentOS Linux release 7.6.1810 (Core)docker 版本&#xff1a;docker-26.1.0.tgz 官网下载地址…

【算法基础实验】图论-基于DFS的连通性检测

基于DFS的连通性检测 理论基础 在图论中&#xff0c;连通分量是无向图的一个重要概念&#xff0c;特别是在处理图的结构和解析图的组成时。连通分组件表示图中的一个子图&#xff0c;在这个子图中任意两个顶点都是连通的&#xff0c;即存在一条路径可以从一个顶点到达另一个顶…

理解归并排序的两种方法(超详细)

目录 前言 一.方法一&#xff1a;归并排序 1.1 归并思路 1.1.1 递归(分解) 1.1.2 区间(排序) 1.1.3 合并拷贝回原数组(合并) 二.归并排序过程 2.1 递归(分解)图解 2.2 归并有序区间(排序)图解 2.2.1 单独一趟排序 2.2.2 有序区间递归排序 2.2.3 数组拷贝(合并) 2.3 归并全部代码…

开箱机选型攻略:如何挑选适合你的自动化设备?

在如今快节奏的生产环境中&#xff0c;自动化设备的运用已成为企业提升效率、降低成本的关键。开箱机作为自动化生产线上的重要一环&#xff0c;其选型对于企业来说至关重要。星派将为您提供一份开箱机选型攻略&#xff0c;帮助您挑选出最适合自己的自动化设备。 一、了解开箱…

从 Apache Doris 到 SelectDB Cloud:云原生架构下的弹性能力揭秘

随着云时代的到来&#xff0c;越来越多企业开始在公有云、私有云乃至 K8s 容器平台构建实时数据平台。云计算基础设施的革新&#xff0c;促使着数据仓库朝着云原生的方向发展。而用户日益复杂的业务负载和降本增效的需求&#xff0c;对于系统资源的精细化管理和成本效益等方面提…

一种利用合法工具渗透的新型方法

摘要 黑客在执行各种攻击步骤时倾向于优先选择合法工具&#xff0c;因为这些工具能帮助他们规避检测系统&#xff0c;同时将恶意软件开发成本降至最低。网络扫描、捕获进程内存转储、数据外泄、远程运行文件&#xff0c;甚至加密驱动器——所有这些都可以通过可信软件完成。为了…

ubuntu查看opencveigen

ubuntu查看opencv&eigen&cmake版本的方法 eigen eigen版本号在/usr/include/eigen3/Eigen/src/Core/util/Macros.h文件中&#xff0c;下图代表版本3.3.7 opencv版本 pkg-config --modversion opencv4也可能最后的字符串是opencv2&#xff0c;opencv

R基本的数据管理

一&#xff0c;创建变量 创建一个数据框 > myData<-data.frame(x1c(1,2,3,4,5,6),x2c(6,5,67,8,9,0)) > myDatax1 x2 1 1 6 2 2 5 3 3 67 4 4 8 5 5 9 6 6 0增加一列为两者的和 > myData$sum<-myData$x1myData$x2 > myDatax1 x2 sum 1 1 6 …