Qt多线程实现方式-moveToThread及其注意事项

news2024/11/28 16:45:58

Qt多线程实现方式-moveToThread及其注意事项

  • Chapter1 Qt多线程实现方式-moveToThread
    • 一、Qt下使用线程主要有两种方法。
    • 二、Qt下创建多线程也有两种方法。
    • 三、其它问题。
  • Chapter2 QT多线程接收串口数据
    • 1.前言
    • 2.功能作用
    • 3.软件测试效果
    • 4.基本步骤
  • Chapter3 利用Qt多线程机制实现双路串口数据流的接收和发送
    • 1. 主程序界面
    • 2. 两个子线程的线程号(调试信息中输出)


特别注意:
通过obj->movetothread(thread)并不是将Object中所有的函数都移动到子线程当中。只有通过槽函数连接的才在子线程中,可以通过qDebug()打印其currentThreadId多试试。

Chapter1 Qt多线程实现方式-moveToThread

原文链接:https://blog.csdn.net/k331922164/article/details/70990239

一、Qt下使用线程主要有两种方法。

一种是传统的继承QThread类,重写run方法。

class WorkerThread : public QThread
 {
     Q_OBJECT
     void run() override {
         QString result;
         /* ... here is the expensive or blocking operation ... */
         emit resultReady(result);
     }
 signals:
     void resultReady(const QString &s);
 };

 void MyObject::startWorkInAThread()
 {
     WorkerThread *workerThread = new WorkerThread(this);
     connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
     connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
     workerThread->start();
 }

该方法已经落伍了,主要原因线程不安全,需要自己手动加锁,比较麻烦,所以推荐使用方法二。

定义一个工作线程(Worker类)继承QObject,在主线程(Controller类)中创建QThread对象、Worker对象,Worker对象调用moveToThread方法。

class Worker : public QObject
 {
     Q_OBJECT

 public slots:
     void doWork(const QString &parameter) {
         QString result;
         /* ... here is the expensive or blocking operation ... */
         emit resultReady(result);
     }

 signals:
     void resultReady(const QString &result);
 };

 class Controller : public QObject
 {
     Q_OBJECT
     QThread workerThread;
 public:
     Controller() {
         Worker *worker = new Worker;
         worker->moveToThread(&workerThread);
         connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
         connect(this, &Controller::operate, worker, &Worker::doWork);
         connect(worker, &Worker::resultReady, this, &Controller::handleResults);
         workerThread.start();
     }
     ~Controller() {
         workerThread.quit();
         workerThread.wait();
     }
 public slots:
     void handleResults(const QString &);
 signals:
     void operate(const QString &);
 };

这样一来,整个Worker对象都移入线程中(线程安全),然后在主线程中每发射一次信号给工作线程,工作线程的槽函数就执行一次。

工作线程执行完,再发射信号到主线程中,以便释放内存。

新建一个Woker对象和一个QThread对象,才能创建一个线程,如果要创建若干多个线程,则需要若干个Woker对象和QThread对象了。

二、Qt下创建多线程也有两种方法。

一种是使用容器(如:QVector类、QList类)去装入多个Worker对象和多个QThread对象,使用[](类似数组的操作),即可访问单个对象。

另一种是使用并发类QtConcurrent。

三、其它问题。

1、使用线程时,能编译通过但是提示段错误,原因是没有在构造函数内new Worker对象和QThread对象。

2、内存泄漏,线程做完时,需要调用quit方法、wait方法,还要delete Worker对象和QThread对象。如果后面还要使用该线程,则再加上new Worker对象和QThread对象。
3、调用任务管理器,可以观察到是否出现内存泄漏。没有任何操作,内存使用量不停增加,即为内存泄漏。

Chapter2 QT多线程接收串口数据

原文链接:https://blog.csdn.net/aptblaze/article/details/118003195

1.前言

QT多线程的使用,和绝大数人一样,犯了错误(请查阅Qt开发人员( Bradley T. Hughes)Blog中的文章 you are-doing-it-wrong介绍)。为了解决问题,网上查阅学习了几十篇文章,基本都是错误的使用方法,或者不完整,未能给予正确的引导。
为方便后来学习者,少走弯路,于是自己动手写了一下程序,过程不再赘述,只以完整的案例进行教学,内部注释较多,可供大家阅读、思考。

2.功能作用

使用多线程,避免上位机软件与单片机等硬件设备高速通讯时,造成软件界面假死、丢包等现象。同时对串口进行了简单的封装,方便调用。本文提供了完整的源代码,方便测试。有较详细的注释方便阅读、思考。编译环境为QT5.8.0,Qt Creator4.2.1

3.软件测试效果

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

4.基本步骤

(1)pro文件添加QT5自带的头文件

QT       += serialport

(2)serialworker.h头文件

#include "serialworker.h"

SerialWorker::SerialWorker(QSerialPort *ser, QObject *parent) : QObject(parent),serial(ser)
{

}
QString SerialWorker::ByteArrayToHexString(QByteArray data)
{
    QString ret(data.toHex().toUpper());
    int len = ret.length()/2;
    qDebug()<<"收到字节长度为:"<<len;
    for(int i=1;i<len;i++)
    {
        ret.insert(2*i+i-1," ");
    }
    return ret;
}

void SerialWorker::doDataReciveWork()
{    
    QByteArray buffer = serial->readAll();
    // 2.进行数据处理
    QString result = ByteArrayToHexString(buffer);
    qDebug() <<  "子线程收到数据:" << result << "线程ID:" << QThread::currentThreadId();
    // 3.将结果发送到主线程
    emit sendResultToGui(result);
}

(3)mainwindow.h文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("子线程读串口");
    this->setMinimumSize(600,248);
    this->setMaximumSize(1200,496);

    InitSerialPortName();

    //1.新建串口处理子线程
    SerialWorker *ser = new SerialWorker(&serial_1);
    ser->moveToThread(&serialThread_1);

   // 2.连接信号和槽
    QString s;
    connect(&serialThread_1, &QThread::finished, ser, &QObject::deleteLater);           // 线程结束,自动删除对象
    connect(&serial_1, &QSerialPort::readyRead, ser, &SerialWorker::doDataReciveWork); // 主线程通知子线程接收数据的信号
    connect(ser, &SerialWorker::sendResultToGui, this, &MainWindow::handleResults);    // 主线程收到数据结果的信号
  //  connect(ser,SIGNAL(sendResultToGui(QString)), this, SLOT(handleResults(QString)));     //主线程收到数据结果的信号写法2

    // 3.开始运行子线程
    serialThread_1.start();                   // 线程开始运行
}

MainWindow::~MainWindow()
{
    serialThread_1.quit();
    serialThread_1.wait();
    delete ui;
}

void MainWindow::InitSerialPortName()
{
    // 清空下拉框
    ui->box_portName->clear();

    //通过QSerialPortInfo查找可用串口
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QString showName = info.portName();
        qDebug() << showName+info.description();
        ui->box_portName->addItem(showName);
    }
    //波特率
        QStringList baudrateList;
        baudrateList<<"4800"<<"9600"<<"19200"<<"38400"<<"57600"<<"115200";
        ui->box_baudrate->addItems(baudrateList);//添加下拉列表选项
        ui->box_baudrate->setCurrentText("115200");//界面中初始值
    //    ui->box_baudrate->setView(new QListView(this));//该设置是配合qss的,不然item行高设置没效果
    //数据位
        QStringList databitList;
        databitList<<"5"<<"6"<<"7"<<"8";
        ui->box_dataBits->addItems(databitList);
        ui->box_dataBits->setCurrentText("8");
//      ui->box_dataBits->setView(new QListView(this));
        //校验位
        QStringList parityList;
        parityList<<"无"<<"奇"<<"偶";
        ui->box_parityBit->addItems(parityList);
        ui->box_parityBit->setCurrentText("No");
//      ui->box_parityBit->setView(new QListView(this));
        //停止位
        QStringList stopbitList;
        stopbitList<<"1"<<"2";
        ui->box_stopBit->addItems(stopbitList);
        ui->box_stopBit->setCurrentText("1");
//      ui->box_stopBit->setView(new QListView(this));
        //流控制
//        QStringList flowctrlList;
//        flowctrlList<<"No"<<"Hardware"<<"Software";
//        ui->boxFlowControl->addItems(flowctrlList);
//        ui->boxFlowControl->setCurrentText("No");
      ui->boxFlowControl->setView(new QListView(this));
}

void MainWindow::on_btn_openPort_clicked()
{
    if(ui->btn_openPort->text()==QString("打开串口"))
    {
        //设置串口名
        QString portName = (ui->box_portName->currentText()).split(":").at(0);
        qDebug() <<"当前打开串口为:"<<portName;
        serial_1.setPortName(portName);

        //设置波特率
        serial_1.setBaudRate(ui->box_baudrate->currentText().toInt());

        //设置停止位
        if(ui->box_stopBit->currentText() == "1")
            serial_1.setStopBits(QSerialPort::OneStop);
        else if(ui->box_stopBit->currentText() == "2")
            serial_1.setStopBits(QSerialPort::TwoStop);

        //设置数据位数
        if(ui->box_dataBits->currentText() == "8")
            serial_1.setDataBits(QSerialPort::Data8);
        else if(ui->box_dataBits->currentText() == "7")
            serial_1.setDataBits(QSerialPort::Data7);
        else if(ui->box_dataBits->currentText() == "6")
            serial_1.setDataBits(QSerialPort::Data6);
        else if(ui->box_dataBits->currentText() == "5")
            serial_1.setDataBits(QSerialPort::Data5);

        //设置奇偶校验
        if(ui->box_parityBit->currentText() == "无")
            serial_1.setParity(QSerialPort::NoParity);
        else if(ui->box_parityBit->currentText() == "偶")
            serial_1.setParity(QSerialPort::EvenParity);
        else if(ui->box_parityBit->currentText() == "奇")
            serial_1.setParity(QSerialPort::OddParity);

//        //设置流控制
//        serial_1.setFlowControl(QSerialPort::NoFlowControl);

        //打开串口
        if(!serial_1.open(QIODevice::ReadWrite))
        {
            QMessageBox::about(NULL, "提示", "无法打开串口!");
            return;
        }

        //下拉菜单控件失能
        ui->box_portName->setEnabled(false);
        ui->box_baudrate->setEnabled(false);
        ui->box_dataBits->setEnabled(false);
        ui->box_parityBit->setEnabled(false);
        ui->box_stopBit->setEnabled(false);

        ui->btn_openPort->setText(QString("关闭串口"));

    }
    else
    {
        //关闭串口
        serial_1.close();
        //下拉菜单控件使能
        ui->box_portName->setEnabled(true);
        ui->box_baudrate->setEnabled(true);
        ui->box_dataBits->setEnabled(true);
        ui->box_parityBit->setEnabled(true);
        ui->box_stopBit->setEnabled(true);
        ui->btn_openPort->setText(QString("打开串口"));
    }
}

void MainWindow::on_btn_clearText_clicked()
{
    ui->browser_dataReceive->clear();
}

void MainWindow::handleResults(const QString &result)
{
    qDebug() <<  "主线程收到结果数据:" << result << "线程ID:" << QThread::currentThreadId();
    //从界面中读取以前收到的数据
    QString oldString = ui->browser_dataReceive->toPlainText()+'\n';
    oldString = oldString + QString(result);
    //清空以前的显示
    ui->browser_dataReceive->clear();
    //重新显示
    ui->browser_dataReceive->append(oldString);
}

(4)mainwindow.cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("子线程读串口");
    this->setMinimumSize(600,248);
    this->setMaximumSize(1200,496);

    InitSerialPortName();

    //1.新建串口处理子线程
    SerialWorker *ser = new SerialWorker(&serial_1);
    ser->moveToThread(&serialThread_1);

   // 2.连接信号和槽
    QString s;
    connect(&serialThread_1, &QThread::finished, ser, &QObject::deleteLater);           // 线程结束,自动删除对象
    connect(&serial_1, &QSerialPort::readyRead, ser, &SerialWorker::doDataReciveWork); // 主线程通知子线程接收数据的信号
    connect(ser, &SerialWorker::sendResultToGui, this, &MainWindow::handleResults);    // 主线程收到数据结果的信号
  //  connect(ser,SIGNAL(sendResultToGui(QString)), this, SLOT(handleResults(QString)));     //主线程收到数据结果的信号写法2

    // 3.开始运行子线程
    serialThread_1.start();                   // 线程开始运行
}

MainWindow::~MainWindow()
{
    serialThread_1.quit();
    serialThread_1.wait();
    delete ui;
}

void MainWindow::InitSerialPortName()
{
    // 清空下拉框
    ui->box_portName->clear();

    //通过QSerialPortInfo查找可用串口
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QString showName = info.portName();
        qDebug() << showName+info.description();
        ui->box_portName->addItem(showName);
    }
    //波特率
        QStringList baudrateList;
        baudrateList<<"4800"<<"9600"<<"19200"<<"38400"<<"57600"<<"115200";
        ui->box_baudrate->addItems(baudrateList);//添加下拉列表选项
        ui->box_baudrate->setCurrentText("115200");//界面中初始值
    //    ui->box_baudrate->setView(new QListView(this));//该设置是配合qss的,不然item行高设置没效果
    //数据位
        QStringList databitList;
        databitList<<"5"<<"6"<<"7"<<"8";
        ui->box_dataBits->addItems(databitList);
        ui->box_dataBits->setCurrentText("8");
//      ui->box_dataBits->setView(new QListView(this));
        //校验位
        QStringList parityList;
        parityList<<"无"<<"奇"<<"偶";
        ui->box_parityBit->addItems(parityList);
        ui->box_parityBit->setCurrentText("No");
//      ui->box_parityBit->setView(new QListView(this));
        //停止位
        QStringList stopbitList;
        stopbitList<<"1"<<"2";
        ui->box_stopBit->addItems(stopbitList);
        ui->box_stopBit->setCurrentText("1");
//      ui->box_stopBit->setView(new QListView(this));
        //流控制
//        QStringList flowctrlList;
//        flowctrlList<<"No"<<"Hardware"<<"Software";
//        ui->boxFlowControl->addItems(flowctrlList);
//        ui->boxFlowControl->setCurrentText("No");
      ui->boxFlowControl->setView(new QListView(this));
}

void MainWindow::on_btn_openPort_clicked()
{
    if(ui->btn_openPort->text()==QString("打开串口"))
    {
        //设置串口名
        QString portName = (ui->box_portName->currentText()).split(":").at(0);
        qDebug() <<"当前打开串口为:"<<portName;
        serial_1.setPortName(portName);

        //设置波特率
        serial_1.setBaudRate(ui->box_baudrate->currentText().toInt());

        //设置停止位
        if(ui->box_stopBit->currentText() == "1")
            serial_1.setStopBits(QSerialPort::OneStop);
        else if(ui->box_stopBit->currentText() == "2")
            serial_1.setStopBits(QSerialPort::TwoStop);

        //设置数据位数
        if(ui->box_dataBits->currentText() == "8")
            serial_1.setDataBits(QSerialPort::Data8);
        else if(ui->box_dataBits->currentText() == "7")
            serial_1.setDataBits(QSerialPort::Data7);
        else if(ui->box_dataBits->currentText() == "6")
            serial_1.setDataBits(QSerialPort::Data6);
        else if(ui->box_dataBits->currentText() == "5")
            serial_1.setDataBits(QSerialPort::Data5);

        //设置奇偶校验
        if(ui->box_parityBit->currentText() == "无")
            serial_1.setParity(QSerialPort::NoParity);
        else if(ui->box_parityBit->currentText() == "偶")
            serial_1.setParity(QSerialPort::EvenParity);
        else if(ui->box_parityBit->currentText() == "奇")
            serial_1.setParity(QSerialPort::OddParity);

//        //设置流控制
//        serial_1.setFlowControl(QSerialPort::NoFlowControl);

        //打开串口
        if(!serial_1.open(QIODevice::ReadWrite))
        {
            QMessageBox::about(NULL, "提示", "无法打开串口!");
            return;
        }

        //下拉菜单控件失能
        ui->box_portName->setEnabled(false);
        ui->box_baudrate->setEnabled(false);
        ui->box_dataBits->setEnabled(false);
        ui->box_parityBit->setEnabled(false);
        ui->box_stopBit->setEnabled(false);

        ui->btn_openPort->setText(QString("关闭串口"));

    }
    else
    {
        //关闭串口
        serial_1.close();
        //下拉菜单控件使能
        ui->box_portName->setEnabled(true);
        ui->box_baudrate->setEnabled(true);
        ui->box_dataBits->setEnabled(true);
        ui->box_parityBit->setEnabled(true);
        ui->box_stopBit->setEnabled(true);
        ui->btn_openPort->setText(QString("打开串口"));
    }
}

void MainWindow::on_btn_clearText_clicked()
{
    ui->browser_dataReceive->clear();
}

void MainWindow::handleResults(const QString &result)
{
    qDebug() <<  "主线程收到结果数据:" << result << "线程ID:" << QThread::currentThreadId();
    //从界面中读取以前收到的数据
    QString oldString = ui->browser_dataReceive->toPlainText()+'\n';
    oldString = oldString + QString(result);
    //清空以前的显示
    ui->browser_dataReceive->clear();
    //重新显示
    ui->browser_dataReceive->append(oldString);
}

(5)serialworker.cpp文件

#include "serialworker.h"

SerialWorker::SerialWorker(QSerialPort *ser, QObject *parent) : QObject(parent),serial(ser)
{

}
QString SerialWorker::ByteArrayToHexString(QByteArray data)
{
    QString ret(data.toHex().toUpper());
    int len = ret.length()/2;
    qDebug()<<"收到字节长度为:"<<len;
    for(int i=1;i<len;i++)
    {
        ret.insert(2*i+i-1," ");
    }
    return ret;
}

void SerialWorker::doDataReciveWork()
{    
    QByteArray buffer = serial->readAll();
    // 2.进行数据处理
    QString result = ByteArrayToHexString(buffer);
    qDebug() <<  "子线程收到数据:" << result << "线程ID:" << QThread::currentThreadId();
    // 3.将结果发送到主线程
    emit sendResultToGui(result);
}

Chapter3 利用Qt多线程机制实现双路串口数据流的接收和发送

原文链接:https://blog.csdn.net/SmartTiger_CSL/article/details/104383717

在上一篇文章的基础上,编写了一个对话框程序,可同时收发两路串口数据,每一路串口均在独立的子线程中实现。增加了清空edit的按钮。

1. 主程序界面

在这里插入图片描述

2. 两个子线程的线程号(调试信息中输出)

在这里插入图片描述

主线程的ID号为0x179c,两个串口子线程类的构造均是在主线程中,串口的启动、接收数据均在各自的子线程中,子线程ID号分别在0x14e4和0x5b0。而串口的关闭是在主线程中。这是和connect的配置有关。代码如下:

#include "serialcontroller.h"
#include <QDebug>

SerialController::SerialController(QObject *parent) : QObject(parent)
{
    m_portId = -1;
}

SerialController::~SerialController()
{
    if(m_serialThread.isRunning())
    {
        m_serialPort->closePort();
        delete m_serialPort;
        m_serialThread.quit();
        m_serialThread.wait();
     }

}
void SerialController::initCtrl(int portId,QString portName,long portBaud)
{
    m_portId = portId;
    m_portName = portName;
    m_portBaud = portBaud;
    qDebug()<<"Controller is running in main thread: "<<QThread::currentThreadId();

    //实例对象保存在堆上,没有父对象的指针要想正常销毁,需要将线程的 finished() 信号关联到 QObject 的 deleteLater() 让其在正确的时机被销毁

    m_serialPort = new SerialPort(m_portId,m_portName,m_portBaud);
    //m_serialPort对象不能有父对象。
    m_serialPort->moveToThread(&m_serialThread);

    connect(this,&SerialController::startRunning,m_serialPort,&SerialPort::startPort);
    connect(&m_serialThread,&QThread::finished,m_serialPort,&QObject::deleteLater);
    connect(this,&SerialController::ctrlSendData,m_serialPort,&SerialPort::write_Data);//从主线程发来的数据写入串口
    connect(m_serialPort,&SerialPort::receive_data,this,&SerialController::ctrlReceiveData);//从串口读取的数据发送给主线程
}
void SerialController::startCtrl()
{
    m_serialThread.start();
    emit startRunning();

}
void SerialController::stopCtrl()
{
    if(m_serialThread.isRunning())
    {       
        m_serialPort->closePort();
        m_serialThread.quit();//会自动发送finished信号
        m_serialThread.wait();       
    }
}

串口的启动和接收数据所在的函数startPort与QThread类的connect信号关联,因此是在子线程中执行;而串口的关闭函数closePort没有与connect关联,不是槽函数,是在SerialController类的stopCtrl中执行,SerialController存在于主线中,因此closePort在主线程中执行。
因此,这里验证里上一篇文章中的重点(3):
(3)controller、worker 对象到底在哪个线程?「在哪创建就属于哪」这句话放在任何地方都是适用的。而 moveToThread() 函数的作用是将槽函数在指定的线程中被调用。也就是说controller、worker对象均在主线程中。除了绑定在connect上的槽函数(及槽函数体调用的函数)外,worker的其余函数也在主线程中执行。这个connect发送者可以是SerialController本身(this),也可以是m_serialThread
moveToThread()并不是将整个worker 对象“搬移”到controller线程中,而是将connect中的槽函数放到controller线程中执行。不注意这一点的话,很可能出现“QObject::Cannot create children for a parent that is in a different thread”问题。或者出现“耗时工作代码”仍在主线程中运行的情况。

在这里插入图片描述

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

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

相关文章

Redis与分布式-分布式锁

接上文 Redis与分布式-集群搭建 1.分布式锁 为了解决上述问题&#xff0c;可以利用分布式锁来实现。 重新复制一份redis&#xff0c;配置文件都是刚下载时候的不用更改&#xff0c;然后启动redis服务和redis客户。 redis存在这样的命令&#xff1a;和set命令差不多&#xff0…

《数据结构》之栈和堆结构及JVM简析

导言: 在数据结构中,我们第一了解到了栈或堆栈,它的结构特点是什么呢?先进后出,它的特点有什么用呢?我们在哪里可以使用到栈结构,栈结构那么简单,使用这么久了为什么不用其它结构替代? 一.程序在内存中的分布 作为一个程序猿,我们应该会常常跟代码打交道,那么我们…

【Java】医院智能导诊小程序源码,springboot框架

智能导诊 可以根据用户症状描述精准推荐科室及医生智能学习医院历史数据及自动进行科室对照,与医院的系统连接后,患者可直接完成预约。 一、系统概述 “智能导诊”以人工智能手段为依托&#xff0c;为人们提供智能分诊、问病信息等服务&#xff0c;在一定程度上满足了人们自我…

Spring框架中如何解决日志输出类型的问题

问题的描述 在学习Spring框架的过程中&#xff0c;运行应用程序时&#xff0c;会出现非常详细的日志输出&#xff0c;影响我对于最终输出结果的观察&#xff0c;具体情况见下图&#xff1a; 解决办法 在resource文件夹下&#xff0c;创建log4j.xml配置文件&#xff0c;Spring …

算法-排序算法

0、算法概述 0.1 算法分类 十种常见排序算法可以分为两大类&#xff1a; 比较类排序&#xff1a;通过比较来决定元素间的相对次序&#xff0c;由于其时间复杂度不能突破O(nlogn)&#xff0c;因此也称为非线性时间比较类排序。 非比较类排序&#xff1a;不通过比较来决定元素间…

【vue3】toRef与toRefs的使用,toRef与ref的区别

假期第四篇&#xff0c;对于基础的知识点&#xff0c;我感觉自己还是很薄弱的。 趁着假期&#xff0c;再去复习一遍 1、toRef与toRefs 创建一个ref对象&#xff0c;其value值指向另一个对象中的某个属性 语法&#xff1a;const name toRef&#xff08;person,‘name’&#xf…

【c++随笔07】常量、变量、static

【c随笔07】常量、变量、static 1、常量、变量1.1、声明变量1.2、使用常量 2、static介绍2.1、static 局部变量2.2、static 全局变量2.3、C static静态成员变量2.4、C static静态成员函数详解 原创地址&#xff0c;https://zhengjunxue.blog.csdn.net/article/details/13167770…

偏微分方程的人工智能

9 偏微分方程的人工智能 在本节中&#xff0c;我们详细介绍了用于解决偏微分方程&#xff08;Partial Differential Equations&#xff0c;PDEs&#xff09;的人工智能领域的进展。我们在第9.1节中概述了PDE建模的一般形式&#xff0c;并阐述了在这个背景下使用机器学习方法的…

前端两年半,CSDN创作一周年

文章目录 一、机缘巧合1.1、起因1.2、万事开头难1.3、 何以坚持&#xff1f; 二、收获三、日常四、憧憬 五、总结 一、机缘巧合 1.1、起因 最开始接触CSDN&#xff0c;还是因为同专业的同学&#xff0c;将计算机实验课的实验题&#xff0c;记录总结并发在了专业群里。后来正式…

Airtest1.2.7新增断言API介绍

1. 前言 1.2.7版本的Airtest中&#xff0c;一个很重要的功能是 新增了非常丰富的断言API &#xff0c;今天我们就来详细看一下Airtest都给我们提供了哪些断言语句。 2. 旧版Airtest提供的断言语句 先回顾下&#xff0c;旧版Airtest一直以来&#xff0c;都只给我们提供了2种断言…

软件工程与计算(一)软件工程基础

国庆快乐&#xff0c;今天开始更新《软件工程与计算&#xff08;卷二&#xff09;》的重要知识点内容~ 一.软件 1.软件独立于硬件 早期的软件是为了计算机硬件在研究型项目中而开发制造的&#xff0c;人们使用专门针对于硬件的指令码和汇编语言编写&#xff0c;这也是最早软件…

中文符号雨python

参考地址 字体地址也可以自己找一下资源 import pygame import randomdef main():# 初始化pygamepygame.init()# 默认不全屏fullscreen False# 窗口未全屏宽和高WIDTH, HEIGHT 1000, 600init_width, init_height WIDTH, HEIGHT# 字块大小&#xff0c;宽&#xff0c;高sufac…

【计算机网络】网络层-控制平面(学习笔记)

一、路由原理 1、网络层功能 1&#xff09;数据平面 转发&#xff1a;将分组从路由器的一个输入端口移到合适的输出端口 2&#xff09;控制平面 路由&#xff1a;确定分组从源到目标的路径 2、路由算法 路径长度可靠性延迟带宽负载通信代价 3、路由的原则 1&#xff0…

2021-06-11 51蛋骗鸡用小数点作秒指示,分钟计时.(怎么用二个数码管做分的倒计时,DP亮灭来计秒)

缘由怎么用二个数码管做分的倒计时&#xff0c;DP亮灭来计秒,求思路 - 24小时必答区 #include "REG52.h" sbit K1 P1^5; sbit K2 P1^6; sbit K3 P1^7; sbit BUZ1P1^0; bit k0; unsigned char code SmZiFu[]{63,6,91,79,102,109,125,7,127,111,128};//0-9. unsign…

4. 条件查询

首先区分下match&#xff0c;match_phrase,term, 参考&#xff1a;https://zhuanlan.zhihu.com/p/592767668?utm_id0 1、全量查询分页指定source 示例&#xff1a;请求地址为http://127.0.0.1:9200/students/_search&#xff0c;请求体为&#xff1a; {"query":…

git使用,一点点

查看自己有没有安装git git --version 如果没有安装请执行sudo yum install -y git来安装 git 指令 git log 查看日志 git pull 同步远端和本地仓库 这就是冲突的报错&#xff1a; 所以这个时候你要同步一下git pull

MATLAB中d2d函数用法

目录 语法 说明 示例 重新采样离散时间模型 重新采样已识别的离散时间模型 d2d函数的功能是重新采样离散时间模型。 语法 sys1 d2d(sys, Ts) sys1 d2d(sys, Ts, method) sys1 d2d(sys, Ts, opts) 说明 sys1 d2d(sys, Ts)将离散时间动态系统模型 sys 重新采样&#…

【精品】Springboot 接收发送日期类型的数据

问题 无法请求到后台&#xff0c;后台报错&#xff1a;[Failed to convert property value of type java.lang.String to required type java.time.LocalDateTime for property &#xff1a; 2023-10-02T09:26:16.06908:00 WARN 14296 --- [p-nio-80-exec-1] .w.s.m.s.Defaul…

【中秋国庆不断更】OpenHarmony组件内状态变量使用:@State装饰器

State装饰的变量&#xff0c;或称为状态变量&#xff0c;一旦变量拥有了状态属性&#xff0c;就和自定义组件的渲染绑定起来。当状态改变时&#xff0c;UI会发生对应的渲染改变。 在状态变量相关装饰器中&#xff0c;State是最基础的&#xff0c;使变量拥有状态属性的装饰器&am…

2021-06-09 51单片机:两个独立按键控制一个led,k1按下松开led闪烁三次,k2按下LED闪烁五次

缘由51单片机:两个独立按键控制一个led,k1按下松开led闪烁三次,k2按下LED闪烁五次_嵌入式-CSDN问答 #include "REG52.h" sbit K1 P1^0; sbit K2 P1^1; sbit LEDP0^0; void main() {unsigned char Xd0,ss0;unsigned int wei0;while(1){if(K10&&Xd0){ss3*2;…