QThread之重写run() 实现线程与业务解耦

一、线程的一般实现形式

Qt中线程类QThread有2种使用方式。

1.继承自QThread,重写run()

 class MyThread : public QThread
 {
     Q_OBJECT
 protected:
     virtual void run() override { ... }
 };

我们一般在run()中加个死循环,不停处理业务或者调用相关的方法。这样的话,我们的业务逻辑与线程紧密耦合在一起。

适用场景:

用于一直不停处理业务,而不是处理完一单业务后,就可以休眠的这种场景。

2.moveToThread

使用一个QObject作为Worker,并moveToThread到线程上。

此种方式不在本文讨论范围内,故不赘述。

二、抽象业务逻辑

我们的业务是需要挂在线程上跑的,那么业务应该有个可执行接口,以实现挂接到线程上。那简单,来一个接口。

IRunable.h

class IRunable
{
public:
    virtual ~IRunable() {}
    virtual void exec() = 0;
};

业务,我们也叫它任务,定义2个任务。

class Task1 : public IRunable
{
public:
    virtual void exec() override
    {
        qDebug() << "do Task1...";
    }
};

class Task2 : public IRunable
{
public:
    virtual void exec() override
    {
        qDebug() << "do Task2...";
    }
};

我们这里使用的线程类是Thread,它派生于QThread,只不过重写的是Thread::process(),与使用QThread无异。

我们自定义Executor执行器类,继承自Thread,原理是通过addRunable(IRunable *able)添加可执行对象到QList<IRunable *>里面,然后在线程执行函数process()中,依次执行list中的对象。

Executor.cpp

Executor::Executor(QObject *parent)
    : Thread(parent)
{

}

Executor::~Executor()
{
    Thread::stop();
}

void Executor::addRunable(IRunable *able)
{
    runableList.append(able);
}

void Executor::addRunables(const QList<IRunable *> &ables)
{
    runableList.append(ables);
}

QList<IRunable *> Executor::getRunables()
{
    return runableList;
}

void Executor::process()
{
    foreach (auto able, runableList)
    {
        able->exec();
    }
}

代码比较简单,这样做,可以让任务与线程之间依赖于接口,而不依赖于实现,实现线程与业务逻辑解耦。此后对任务的扩展,不牵涉到线程,且可以实现单线程-多任务,多线程-多任务的灵活配置。

另外,多线程对于共享数据的竞争保护,也可以在各自的任务实现类中,进行处理。

三、测试

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    executor = new Executor();

    runableList.append(new Task1());
    runableList.append(new Task2());
    executor->addRunables(runableList);
}

MainWindow::~MainWindow()
{
    delete executor;
    executor = nullptr;
    qDeleteAll(runableList);
    runableList.clear();
    delete ui;
}

void MainWindow::on_pushButton_start_clicked()
{
    executor->start();
}

void MainWindow::on_pushButton_pause_clicked()
{
    executor->pause();
}

void MainWindow::on_pushButton_stop_clicked()
{
    executor->stop();
}

void MainWindow::on_pushButton_resume_clicked()
{
    executor->resume();
}

void MainWindow::on_pushButton_clicked()
{
    Thread::State s = executor->state();
    if (s == Thread::Stoped)
    {
        qDebug() << "state : Stoped";
    }
    else if (s == Thread::Running)
    {
        qDebug() << "state : Running";
    }
    else if (s == Thread::Paused)
    {
        qDebug() << "state : Paused";
    }
}

运行效果:
在这里插入图片描述



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

本文涉及工程代码,公众号回复:37PauseQThread,即可下载。

在这里插入图片描述

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用 Qt 线程和 Modbus 库实现访问 Modbus 485 读写仪器仪表参数的 C++ 代码示例,不采用继承 QThread 重写 run 的方法实现: ```c++ // modbusworker.h #ifndef MODBUSWORKER_H #define MODBUSWORKER_H #include <QObject> #include <QModbusDevice> #include <QModbusTcpClient> class ModbusWorker : public QObject { Q_OBJECT public: explicit ModbusWorker(QObject *parent = nullptr); void setConnectionParameters(QString host, quint16 port); void setDeviceAddress(int address); bool connectDevice(); void readData(int startAddress, int numberOfValues); void writeData(int startAddress, int value); signals: void dataRead(QVector<quint16> data); void dataWritten(); private: QModbusDevice *modbusDevice; QString connectionHost; quint16 connectionPort; int deviceAddress; }; #endif // MODBUSWORKER_H ``` ```c++ // modbusworker.cpp #include "modbusworker.h" ModbusWorker::ModbusWorker(QObject *parent) : QObject(parent) { modbusDevice = new QModbusTcpClient(this); } void ModbusWorker::setConnectionParameters(QString host, quint16 port) { connectionHost = host; connectionPort = port; } void ModbusWorker::setDeviceAddress(int address) { deviceAddress = address; } bool ModbusWorker::connectDevice() { if (!modbusDevice) return false; modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, connectionHost); modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, connectionPort); modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1"); modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, "EvenParity"); modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, 9600); modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, 8); modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, 1); modbusDevice->setTimeout(1000); modbusDevice->setNumberOfRetries(3); if (!modbusDevice->connectDevice()) { return false; } return true; } void ModbusWorker::readData(int startAddress, int numberOfValues) { if (!modbusDevice) return; if (auto *reply = modbusDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters, startAddress, numberOfValues), deviceAddress)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, this, [=]() { if (reply->error() == QModbusDevice::NoError) { emit dataRead(reply->result().values()); } reply->deleteLater(); }); } else { delete reply; } } else { emit dataRead(QVector<quint16>()); } } void ModbusWorker::writeData(int startAddress, int value) { if (!modbusDevice) return; if (auto *reply = modbusDevice->sendWriteRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters, startAddress, 1), QVector<quint16>{static_cast<quint16>(value)}, deviceAddress)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, this, [=]() { if (reply->error() == QModbusDevice::NoError) { emit dataWritten(); } reply->deleteLater(); }); } else { delete reply; } } else { emit dataWritten(); } } ``` ```c++ // mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "modbusworker.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_connectPushButton_clicked(); void on_readPushButton_clicked(); void on_writePushButton_clicked(); void on_dataRead(QVector<quint16> data); void on_dataWritten(); private: Ui::MainWindow *ui; ModbusWorker *modbusWorker; }; #endif // MAINWINDOW_H ``` ```c++ // mainwindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include <QMessageBox> #include <QThread> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , modbusWorker(new ModbusWorker(this)) { ui->setupUi(this); connect(modbusWorker, &ModbusWorker::dataRead, this, &MainWindow::on_dataRead); connect(modbusWorker, &ModbusWorker::dataWritten, this, &MainWindow::on_dataWritten); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_connectPushButton_clicked() { if (ui->tcpConnectionRadioButton->isChecked()) { modbusWorker->setConnectionParameters(ui->hostLineEdit->text(), ui->portSpinBox->value()); } else if (ui->serialConnectionRadioButton->isChecked()) { // Set serial connection parameters } modbusWorker->setDeviceAddress(ui->deviceAddressSpinBox->value()); if (!modbusWorker->connectDevice()) { QMessageBox::warning(this, "Error", "Unable to connect to Modbus device."); } } void MainWindow::on_readPushButton_clicked() { modbusWorker->readData(ui->startAddressSpinBox->value(), ui->numberOfValuesSpinBox->value()); } void MainWindow::on_writePushButton_clicked() { modbusWorker->writeData(ui->startAddressSpinBox->value(), ui->writeValueSpinBox->value()); } void MainWindow::on_dataRead(QVector<quint16> data) { // Process read data } void MainWindow::on_dataWritten() { // Process write data } ``` 在主界面中,通过连接不同的按钮和输入框,调用 ModbusWorker 类中的函数,实现连接 Modbus 设备、读取数据、写入数据等操作。而 ModbusWorker 类中的函数则使用 Qt Modbus 库提供的类和函数,实现连接 Modbus 设备、读写数据等操作,同时使用信号与槽机制将数据传递到主线程进行处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值