一般我们在用Qt开发时,把耗时操作放在线程中执行,避免卡界面,Qt的线程使用有两种方式,一种是继承QThread,一种是moveToThread的方式,以及QtConcurrent方式
首先我们来看第一种:
#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H#include <QDebug>
#include <QThread>
#include <QDateTime>#define PRINTTIME QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz");class WorkerThread : public QThread
{Q_OBJECT
public:explicit WorkerThread(QObject *parent = nullptr);int getThreadFun();void run() override{QString result = QString("WorkerThread");/* ... here is the expensive or blocking operation ... */QThread::sleep(3);qDebug() << "WorkerThread::run===============currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;emit resultReady(result);}signals:void resultReady(const QString &result);};
#endif // WORKERTHREAD_H
#include "workerthread.h"WorkerThread::WorkerThread(QObject *parent) : QThread(parent)
{}int WorkerThread::getThreadFun()
{qDebug() << "Dialog::getThreadFun============currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;int id = (int)QThread::currentThreadId();QThread::sleep(2);return id;}
使用线程:
void Dialog::initData()
{m_workerThread = new WorkerThread(this);connect(m_workerThread, &WorkerThread::resultReady, this, &Dialog::handleResults1);//connect(m_workerThread, &WorkerThread::finished, m_workerThread, &Dialog::deleteLater);//m_workerThread->start();connect(ui->btn1, SIGNAL(clicked()), this, SLOT(slotBtn1()));
}void Dialog::slotBtn1()
{qDebug() << "Dialog::slotBtn1================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;m_workerThread->start();qDebug() << "Dialog::slotBtn1================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;m_workerThread->getThreadFun();
}
运行结果:
从以上信息可以看到,只有run函数里的执行是属于子线程,getThreadFun函数虽然是WorkerThread类的,但执行起来并不属于子线程,它的线程号与主线程一样,同时,开启线程后还没等结果就执行下面的语句了。run函数执行完,线程就结束了。
那么有没有一种情况,线程一直处理运行中呢?下面看第2种线程的方式:
#ifndef WORKER_H
#define WORKER_H#include <QObject>
#include <QDebug>
#include <QThread>
#include <QDateTime>#define PRINTTIME QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz");class Worker : public QObject
{Q_OBJECT
public:explicit Worker(QObject *parent = nullptr);int getThreadFun();public slots:void doWork(const QString ¶meter){QString result = QString("currentThreadId==%1").arg((int)QThread::currentThreadId());/* ... here is the expensive or blocking operation ... */QThread::sleep(3);qDebug() << "Worker::doWork==================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;emit resultReady(result);}signals:void resultReady(const QString &result);
};#endif // WORKER_H
#include "worker.h"Worker::Worker(QObject *parent): QObject{parent}
{}int Worker::getThreadFun()
{qDebug() << "Worker::getThreadFun============currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;int id = (int)QThread::currentThreadId();QThread::sleep(2);return id;}
使用线程:
void Dialog::initData()
{m_worker = new Worker;m_worker->moveToThread(&workerThread);connect(&workerThread, &QThread::finished, m_worker, &QObject::deleteLater);connect(this, &Dialog::operate, m_worker, &Worker::doWork);connect(m_worker, &Worker::resultReady, this, &Dialog::handleResults2);workerThread.start();connect(ui->btn2, SIGNAL(clicked()), this, SLOT(slotBtn2()));
}void Dialog::slotBtn2()
{qDebug() << "Dialog::slotBtn2=======1========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;emit operate("Worker");qDebug() << "Dialog::slotBtn2=======2========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;m_worker->getThreadFun();
}void Dialog::handleResults2()
{qDebug() << "Dialog::handleResults2==========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;
}
运行结果:
从上面结果也可以看出,由operate信号触发的槽函数是子线程,直接调用getThreadFun函数还是属于子线程,但这个与第一种线程的方案不同在于,线程一起处理运行中,只要触发operate信号都会执行槽函数的子线程,以上两种情况都是线程还没执行完,调用线程的函数就已经结束了。
但有时候我们需要函数的结果,并且根据结果执行不同的分支要求,那边没有一种方案可以这种做呢,下面看第3种线程方式,使用QtConcurrent获取线程执行的返回结果:
#include "dialog.h"
#include "ui_dialog.h"
#include "workerthread.h"
#include "worker.h"
#include <QDebug>
#include <QSerialPort>
#include <QtConcurrent>
#include <QTime>
#include <QDebug>
#include <QEventLoop>
#include <QtConcurrentMap>
#include <QSerialPortInfo>using namespace QtConcurrent;#define PRINTTIME QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz");Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog)
{ui->setupUi(this);initData();}Dialog::~Dialog()
{workerThread.quit();workerThread.wait();m_workerThread->quit();m_workerThread->wait();delete ui;
}void Dialog::initData()
{connect(ui->btn3, SIGNAL(clicked()), this, SLOT(slotBtn3()));
}int threadFun3(int a1, int a2)
{qDebug() << "threadFun3======================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;QThread::sleep(2);qDebug() << "threadFun3======================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;return a1 + a2;
}void Dialog::slotBtn3()
{int a1 = 5092;int a2 = 542451;qDebug() << "Dialog::slotBtn3=======1========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;QFuture<int> future =QtConcurrent::run(threadFun3, a1, a2);int result = future.result();qDebug() << "Dialog::slotBtn3=======2========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;qDebug() << "Dialog::slotBtn3================result================" << result << PRINTTIME;
}
运行结果:
这种方案可以获取线程执行返回的结果了,但是这也存在一个问题,这个返回结果是待slotBtn3函数调用完了才返回,有时我们需要我等待这个结果再执行下面的代码。这时候就需要采用另外一种 方式下了。直接上代码:
void Dialog::initData()
{connect(ui->btn4, SIGNAL(clicked()), this, SLOT(slotBtn4()));
}//这里检测串口连接设备,连上设备才算打开串口成功
QString checkDeviceConnectPort()
{QString openName = "";QString tempData{""};QThread::sleep(2);qDebug() << "checkDeviceConnectPort======================currentThreadId=====" << QThread::currentThreadId() << PRINTTIME;QSerialPort *serialPort = new QSerialPort();foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){serialPort->setPort(info);if(info.portName().contains("Bluetooth", Qt::CaseInsensitive))continue;bool isOpen = serialPort->open(QIODevice::ReadWrite);qDebug() << "checkDeviceConnectPort(================isOpen==" << isOpen << info.portName() << PRINTTIME;if(isOpen){//设置参数serialPort->setBaudRate(115200); //波特率115200serialPort->setDataBits(QSerialPort::Data8); //数据位8serialPort->setStopBits(QSerialPort::OneStop); //停止位1serialPort->setParity(QSerialPort::NoParity); //校验位 无serialPort->setFlowControl(QSerialPort::NoFlowControl); //设置为无流控制serialPort->setReadBufferSize(40960); //最大缓存40960QByteArray sendData("XX\n");//sendData的数据// 写入发送缓存区qint64 sendDataLen = serialPort->write(sendData);qDebug() << "checkDeviceConnectPort================sendDataLen=====" << sendDataLen;qApp->processEvents();qDebug() << "SerialPortManage::checkDeviceConnectPort=================processEvents================" << PRINTTIME;while(serialPort->isOpen() && serialPort->waitForReadyRead(3000)) {QString array = serialPort->readAll();qDebug() << "SerialPortManage::checkDeviceConnectPort=====================array============" << array << PRINTTIME;tempData.append(array);if(array.isEmpty()) {qApp->processEvents();}if(tempData.contains("end")){qDebug() << "checkDeviceConnectPort=================tempData================" << tempData;break;}}if(openName.size() > 0){break;}}} //end foreach(qDebug() << "checkDeviceConnectPort=================openName=================" << openName << PRINTTIME;serialPort->close();serialPort->deleteLater();return openName;
}void Dialog::slotBtn4()
{qDebug() << "Dialog::slotBtn4===1========================currentThreadId=====" << QThread::currentThreadId() << PRINTTIME;QFuture<QString> future = QtConcurrent::run(checkDeviceConnectPort);while (!future.isFinished()) {QApplication::processEvents(QEventLoop::AllEvents, 30);}qDebug() << "Dialog::slotBtn4============================result==============" << future.result() << PRINTTIME;qDebug() << "Dialog::slotBtn4===2========================currentThreadId=====" << QThread::currentThreadId() << PRINTTIME;}
运行结果:
从上面可以看到,待线程函数执行完了,才会执行
qDebug() << "Dialog::slotBtn4============================result==============" << future.result() << PRINTTIME;
qDebug() << "Dialog::slotBtn4===2========================currentThreadId=====" << QThread::currentThreadId() << PRINTTIME;
的代码,这样就可以根据结果处理后续的逻辑了,同时也不会卡界面。
完整代码中下:
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QThread>QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACEclass WorkerThread;
class Worker;class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = nullptr);~Dialog();void initData();public slots:void slotBtn1();void slotBtn2();void slotBtn3();void slotBtn4();void handleResults1();void handleResults2();signals:void operate(const QString &result);private:Ui::Dialog *ui;WorkerThread *m_workerThread{nullptr};QThread workerThread;Worker *m_worker{nullptr};
};
#endif // DIALOG_H
dialog.cpp文件
#include "dialog.h"
#include "ui_dialog.h"
#include "workerthread.h"
#include "worker.h"
#include <QDebug>
#include <QSerialPort>
#include <QtConcurrent>
#include <QTime>
#include <QDebug>
#include <QEventLoop>
#include <QtConcurrentMap>
#include <QSerialPortInfo>using namespace QtConcurrent;#define PRINTTIME QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz");Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog)
{ui->setupUi(this);initData();}Dialog::~Dialog()
{workerThread.quit();workerThread.wait();m_workerThread->quit();m_workerThread->wait();delete ui;
}void Dialog::initData()
{m_workerThread = new WorkerThread(this);connect(m_workerThread, &WorkerThread::resultReady, this, &Dialog::handleResults1);//connect(m_workerThread, &WorkerThread::finished, m_workerThread, &Dialog::deleteLater);//m_workerThread->start();m_worker = new Worker;m_worker->moveToThread(&workerThread);connect(&workerThread, &QThread::finished, m_worker, &QObject::deleteLater);connect(this, &Dialog::operate, m_worker, &Worker::doWork);connect(m_worker, &Worker::resultReady, this, &Dialog::handleResults2);workerThread.start();connect(ui->btn1, SIGNAL(clicked()), this, SLOT(slotBtn1()));connect(ui->btn2, SIGNAL(clicked()), this, SLOT(slotBtn2()));connect(ui->btn3, SIGNAL(clicked()), this, SLOT(slotBtn3()));connect(ui->btn4, SIGNAL(clicked()), this, SLOT(slotBtn4()));
}void Dialog::slotBtn1()
{qDebug() << "Dialog::slotBtn1================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;m_workerThread->start();qDebug() << "Dialog::slotBtn1================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;m_workerThread->getThreadFun();
}void Dialog::slotBtn2()
{qDebug() << "Dialog::slotBtn2=======1========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;emit operate("Worker");qDebug() << "Dialog::slotBtn2=======2========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;m_worker->getThreadFun();
}int threadFun3(int a1, int a2)
{qDebug() << "threadFun3======================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;QThread::sleep(2);qDebug() << "threadFun3======================currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;return a1 + a2;
}void Dialog::slotBtn3()
{int a1 = 5092;int a2 = 542451;QString str = "AAAAAAAAAAAA";qDebug() << "Dialog::slotBtn3=======1========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;QFuture<int> future =QtConcurrent::run(threadFun3, a1, a2);int result = future.result();qDebug() << "Dialog::slotBtn3=======2========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;qDebug() << "Dialog::slotBtn3================result================" << result << PRINTTIME;
}//这里检测串口连接设备,连上设备才算打开串口成功
QString checkDeviceConnectPort()
{QString openName = "";QString tempData{""};QThread::sleep(2);qDebug() << "checkDeviceConnectPort======================currentThreadId=====" << QThread::currentThreadId() << PRINTTIME;QSerialPort *serialPort = new QSerialPort();foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){serialPort->setPort(info);if(info.portName().contains("Bluetooth", Qt::CaseInsensitive))continue;bool isOpen = serialPort->open(QIODevice::ReadWrite);qDebug() << "checkDeviceConnectPort(================isOpen==" << isOpen << info.portName() << PRINTTIME;if(isOpen){//设置参数serialPort->setBaudRate(115200); //波特率115200serialPort->setDataBits(QSerialPort::Data8); //数据位8serialPort->setStopBits(QSerialPort::OneStop); //停止位1serialPort->setParity(QSerialPort::NoParity); //校验位 无serialPort->setFlowControl(QSerialPort::NoFlowControl); //设置为无流控制serialPort->setReadBufferSize(40960); //最大缓存40960QByteArray sendData("XX\n");//sendData的数据// 写入发送缓存区qint64 sendDataLen = serialPort->write(sendData);qDebug() << "checkDeviceConnectPort================sendDataLen=====" << sendDataLen;qApp->processEvents();qDebug() << "SerialPortManage::checkDeviceConnectPort=================processEvents================" << PRINTTIME;while(serialPort->isOpen() && serialPort->waitForReadyRead(3000)) {QString array = serialPort->readAll();qDebug() << "SerialPortManage::checkDeviceConnectPort=====================array============" << array << PRINTTIME;tempData.append(array);if(array.isEmpty()) {qApp->processEvents();}if(tempData.contains("end")){qDebug() << "checkDeviceConnectPort=================tempData================" << tempData;break;}}if(openName.size() > 0){break;}}} //end foreach(qDebug() << "checkDeviceConnectPort=================openName=================" << openName << PRINTTIME;serialPort->close();serialPort->deleteLater();return openName;
}void Dialog::slotBtn4()
{qDebug() << "Dialog::slotBtn4===1========================currentThreadId=====" << QThread::currentThreadId() << PRINTTIME;QFuture<QString> future = QtConcurrent::run(checkDeviceConnectPort);while (!future.isFinished()) {QApplication::processEvents(QEventLoop::AllEvents, 30);}qDebug() << "Dialog::slotBtn4============================result==============" << future.result() << PRINTTIME;qDebug() << "Dialog::slotBtn4===2========================currentThreadId=====" << QThread::currentThreadId() << PRINTTIME;}void Dialog::handleResults1()
{qDebug() << "Dialog::handleResults1==========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;
}void Dialog::handleResults2()
{qDebug() << "Dialog::handleResults2==========currentThreadId=======" << QThread::currentThreadId() << PRINTTIME;
}
QT += core gui serialport concurrentgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++17# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \dialog.cpp \worker.cpp \workerthread.cppHEADERS += \dialog.h \worker.h \workerthread.hFORMS += \dialog.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
UI布局:
参考:
QFutureWatcher获取QtConcurrent::run线程函数的返回值_qt怎样异步获取qtconcurrent::run创建的线程的返回结果-CSDN博客