QT的Qthread机制

​​

目录

QT的Qthread的一些机制和用法

  最后,简单的总结一哈,QThread的用途!


QT的Qthread的一些机制和用法

  • QT提供QThread类以进行多任务处理。与多任务处理一样,Qt提供的线程可以做到单个线程做不到的事情。例如,网络应用程序中,可以使用线程处理多种连接器。
  • QThread继承自QObject类,且提供QMutex类以实现同步。线程和进程共享全局变量,可以使用互斥体对改变后的全局变量值实现同步。因此,必须编辑全局数据时,使用互斥体实现同步,其它进程则不能改变或浏览全局变量值。
  • Qt中实现多线程有两种方法:一种是派生QThread类对象的方法,即自定义一个类继承自QThread,并重写run()函数,在run()函数中实现线程要执行的任务;另一种是使用QThread对象的方法,即创建一个QThread对象,并将一个继承自QObject的类的对象移动到该线程中,在该类中定义信号和槽函数来实现线程间的通信和任务执行。
  • Qt中的线程可以设置优先级、退出、等待、终止等操作,也可以通过信号和槽机制来监听线程的开始和结束事件。Qt还提供了一些静态函数来获取当前线程、理想线程数、线程休眠等信息。
  • Qt还提供了一些其他的类来辅助线程的管理和同步,如QRunnable、QThreadPool、QFuture、QSemaphore、QWaitCondition等。

下面,我们就来演示一哈,如何使用QThread(例子1):

  • 首先,定义一个Worker类,继承自QObject,并声明一个槽函数doWork()和一个信号resultReady()。
  • 然后,在Worker类的构造函数中,使用qDebug()输出当前线程的地址,以便观察线程的变化。
  • 接着,在doWork()槽函数中,使用QThread::sleep()模拟一个耗时操作,并在完成后发射resultReady()信号。
  • 然后,创建一个QThread对象和一个Worker对象,并使用QObject::moveToThread()将Worker对象移动到QThread对象中。
  • 接着,使用QObject::connect()连接QThread对象的started()信号和Worker对象的doWork()槽函数,以及Worker对象的resultReady()信号和QThread对象的quit()槽函数。
  • 最后,使用QThread::start()启动线程,并使用QThread::wait()等待线程结束!

具体的代码实现如下:

#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr) : QObject(parent)
    {
        qDebug() << "Worker created in thread" << QThread::currentThread();
    }

public slots:
    void doWork()
    {
        qDebug() << "Worker started in thread" << QThread::currentThread();
        QThread::sleep(5); // simulate a heavy task
        qDebug() << "Worker finished in thread" << QThread::currentThread();
        emit resultReady();
    }

signals:
    void resultReady();
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "Main thread" << QThread::currentThread();

    QThread *thread = new QThread(); // create a thread object
    Worker *worker = new Worker(); // create a worker object
    worker->moveToThread(thread); // move the worker to the thread

    QObject::connect(thread, &QThread::started, worker, &Worker::doWork); // connect the thread started signal to the worker doWork slot
    QObject::connect(worker, &Worker::resultReady, thread, &QThread::quit); // connect the worker resultReady signal to the thread quit slot
    QObject::connect(thread, &QThread::finished, worker, &Worker::deleteLater); // connect the thread finished signal to the worker deleteLater slot
    QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); // connect the thread finished signal to the thread deleteLater slot

    thread->start(); // start the thread
    thread->wait(); // wait for the thread to finish

    qDebug() << "Main thread finished";

    return a.exec();
}

运行上面的代码,结果如下:

Main thread QThread(0x7ffedf9a1b80)
Worker created in thread QThread(0x7ffedf9a1b80)
Worker started in thread QThread(0x55c3c8f3c4c0)
Worker finished in thread QThread(0x55c3c8f3c4c0)
Main thread finished

可以看到,Worker对象最初是在主线程中创建的,但是在执行doWork()槽函数时,已经移动到了子线程中。当doWork()槽函数完成后,发射了resultReady()信号,导致子线程退出,并删除了Worker对象和子线程对象。主线程在等待子线程结束后,也正常退出了。这就是一个简单的多线程的例子。希望对你有帮助。😊

总的来说Qthread的实现是:

  • 创建一个QThread对象和一个继承自QObject的类的对象,这个类中定义了要在线程中执行的任务和信号。
  • 使用QObject::moveToThread()将继承自QObject的类的对象移动到QThread对象中,这样就可以在子线程中执行任务了。
  • 使用QObject::connect()连接QThread对象的信号和继承自QObject的类的对象的槽函数,以及继承自QObject的类的对象的信号和QThread对象的槽函数,以实现线程间的通信和控制。
  • 使用QThread::start()启动线程,并使用QThread::wait()等待线程结束。

下面是一个使用QThread的计时器触发的例子(例子2):

  • 首先,定义一个TimerWorker类,继承自QObject,并声明一个槽函数onTimeout()和一个信号timeout()。
  • 然后,在TimerWorker类的构造函数中,创建一个QTimer对象,并将其timeout()信号连接到自身的onTimeout()槽函数和timeout()信号。
  • 接着,在onTimeout()槽函数中,使用qDebug()输出当前线程的地址和当前时间,以便观察计时器的触发情况。
  • 然后,创建一个QThread对象和一个TimerWorker对象,并使用QObject::moveToThread()将TimerWorker对象移动到QThread对象中。
  • 接着,使用QObject::connect()连接QThread对象的started()信号和TimerWorker对象的start()槽函数,以及TimerWorker对象的timeout()信号和主线程的槽函数。
  • 最后,使用QThread::start()启动线程,并使用QThread::wait()等待线程结束。

代码如下:

#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
#include <QDateTime>

class TimerWorker : public QObject
{
    Q_OBJECT
public:
    explicit TimerWorker(QObject *parent = nullptr) : QObject(parent)
    {
        qDebug() << "TimerWorker created in thread" << QThread::currentThread();
        m_timer = new QTimer(this); // create a timer
        m_timer->setInterval(1000); // set the interval to 1 second
        connect(m_timer, &QTimer::timeout, this, &TimerWorker::onTimeout); // connect the timer timeout signal to the onTimeout slot
        connect(m_timer, &QTimer::timeout, this, &TimerWorker::timeout); // connect the timer timeout signal to the timeout signal
    }

public slots:
    void start()
    {
        m_timer->start(); // start the timer
    }

    void onTimeout()
    {
        qDebug() << "TimerWorker timeout in thread" << QThread::currentThread();
        qDebug() << "Current time:" << QDateTime::currentDateTime().toString();
    }

signals:
    void timeout();
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "Main thread" << QThread::currentThread();

    QThread *thread = new QThread(); // create a thread object
    TimerWorker *worker = new TimerWorker(); // create a worker object
    worker->moveToThread(thread); // move the worker to the thread

    QObject::connect(thread, &QThread::started, worker, &TimerWorker::start); // connect the thread started signal to the worker start slot
    QObject::connect(worker, &TimerWorker::timeout, [](){qDebug() << "Main thread received timeout signal";}); // connect the worker timeout signal to a lambda function in the main thread
    QObject::connect(thread, &QThread::finished, worker, &TimerWorker::deleteLater); // connect the thread finished signal to the worker deleteLater slot
    QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); // connect the thread finished signal to the thread deleteLater slot

    thread->start(); // start the thread
    thread->wait(5000); // wait for 5 seconds
    thread->quit(); // quit the thread

    qDebug() << "Main thread finished";

    return a.exec();
}

运行结果如下:

Main thread QThread(0x7ffedf9a1b80)
TimerWorker created in thread QThread(0x7ffedf9a1b80)
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:23"
Main thread received timeout signal
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:24"
Main thread received timeout signal
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:25"
Main thread received timeout signal
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:26"
Main thread received timeout signal
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:27"
Main thread received timeout signal
Main thread finished

可以看到,TimerWorker对象最初是在主线程中创建的,但是在执行onTimeout()槽函数时,已经移动到了子线程中。每隔一秒,计时器就会触发一次timeout()信号,导致onTimeout()槽函数和主线程的lambda函数被执行。主线程在等待5秒后,退出了子线程,并删除了TimerWorker对象和子线程对象。这就是一个使用QThread的计时器触发的例子。

这里再举一个用QThread实现一个子线程调用摄像头的程序(例子3

  • 首先,使用Qt Designer创建一个UI界面,添加一个QLabel控件用来显示摄像头的画面,添加一个QPushButton控件用来启动和停止摄像头,添加一个QComboBox控件用来选择摄像头的索引号。
  • 然后,定义一个MainWindow类,继承自QMainWindow,并使用Ui_MainWindow类来加载UI界面。
  • 然后,在MainWindow类的构造函数中,创建一个QCamera对象,并将其setViewfinder()函数设置为QLabel控件的地址,这样就可以将摄像头的画面显示到QLabel控件上。
  • 接着,在MainWindow类中,定义一个槽函数on_pushButton_clicked(),用来处理QPushButton控件的clicked()信号,根据QPushButton控件的文本判断是启动还是停止摄像头,并改变QPushButton控件的文本。
  • 接着,在MainWindow类中,定义一个槽函数on_comboBox_currentIndexChanged(),用来处理QComboBox控件的currentIndexChanged()信号,根据QComboBox控件的当前索引值来设置QCamera对象的摄像头索引号,并重新启动摄像头。

代码如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCamera>
#include <QCameraInfo>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // create a camera object
    camera = new QCamera(this);
    // set the viewfinder to the label widget
    camera->setViewfinder(ui->label);
    // get the available cameras
    QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
    // add the camera names to the combo box
    foreach (const QCameraInfo &cameraInfo, cameras) {
        ui->comboBox->addItem(cameraInfo.description());
    }
}

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

// slot function for the push button
void MainWindow::on_pushButton_clicked()
{
    // if the button text is "Start"
    if (ui->pushButton->text() == "Start") {
        // start the camera
        camera->start();
        // change the button text to "Stop"
        ui->pushButton->setText("Stop");
    }
    // else if the button text is "Stop"
    else if (ui->pushButton->text() == "Stop") {
        // stop the camera
        camera->stop();
        // change the button text to "Start"
        ui->pushButton->setText("Start");
    }
}

// slot function for the combo box
void MainWindow::on_comboBox_currentIndexChanged(int index)
{
    // stop the camera
    camera->stop();
    // set the camera device index to the combo box current index
    camera->setCaptureMode(QCamera::CaptureVideo);
    camera->setCaptureMode(QCamera::CaptureStillImage);
    camera->setCaptureMode(QCamera::CaptureViewfinder);
    camera->setViewfinderSettings(QCameraViewfinderSettings());
    
    camera->setViewfinder(ui->label);
    
    QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
    
    foreach (const QCameraInfo &cameraInfo, cameras) {
        if (cameraInfo.description() == ui->comboBox->currentText()) {
            camera = new QCamera(cameraInfo);
            break;
        }
        
        delete camera;
        
        camera = new QCamera(cameras.at(index));
        
        break;
        
        // start the camera
        camera->start();
        
        // change the button text to "Stop"
        ui->pushButton->setText("Stop");
        
}

  最后,简单的总结一哈,QThread的用途

        使用QThread类对象来实现多线程的用途很多,比如:

  • 可以在子线程中执行耗时的操作,如文件读写、网络请求、数据处理等,避免阻塞主线程的用户界面。
  • 可以在子线程中创建和使用继承自QObject的类的对象,如QTimer、QTcpSocket、QUdpSocket、QProcess等,利用信号和槽机制来实现异步的事件处理。
  • 可以在子线程中使用QEventLoop类来创建一个事件循环,以便在子线程中处理事件和信号。(Qt的事件循环是一种机制,用来接收和处理来自操作系统或程序框架的各种事件,如用户输入、窗口绘制、计时器触发等。
  • 可以在子线程中使用QThreadStorage类来为每个线程创建一个独立的存储空间,以存储线程相关的数据。
  • 可以在子线程中使用QFuture、QFutureWatcher、QFutureSynchronizer等类来实现并行计算和异步结果的获取。

        这些都是使用QThread类对象来实现多线程的一些常见用途。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Helloorld_11

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

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

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

打赏作者

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

抵扣说明:

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

余额充值