Qt多线程

序言

多线程一般用在:
在QT的 UI 编程中, 如果有一个函数消耗的时间特别长, 并且运行于主线程, 那么界面的响应会很不灵敏.。比如说 如果该函数执行时间很长, 为了通知任务的进度, 一般会使用进度条. 但有时候无法准确的使用进度条, 比如在数据库操作中, 为了提高读写数据库的性能, 通常会采用事务操作, 多个读写数据库的操作合并成了一个事务, 此时如何设置进度条的进度值。

Qt 多线程常见得三种方法:

  1. 继承自QThread.
  2. moveToThread.
  3. QtConcurrent::run().

方法

继承QThread

说明

  1. 自定义继承QThread类,需要重写run函数,去依据需要实现自己的方法。
  2. 调用start() 启动。

代码

MyThread

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include 
#include 
class MyThread : public QThread
{
    Q_OBJECT
public:
   explicit MyThread(QObject* parent = nullptr);
    ~MyThread();
signals:
    void SigMyThread(int para);
public slots:
    void onMyThread(int para);
protected:
    void run() override;

};

#endif // MYTHREAD_H
#include "mythread.h"
#include 
MyThread::MyThread(QObject* parent)
    :QThread (parent)
{

}

MyThread::~MyThread()
{

}

void MyThread::onMyThread(int para)
{
     qDebug()<< __FUNCTION__ <<"current thread ID:"<<QThread::currentThreadId();
     int count = 888;
     for(int i = 0;i!=1000000;++i)
     {
         ++count;
     }
}

void MyThread::run()
{
    qDebug()<< __FUNCTION__ <<"current thread ID:"<<QThread::currentThreadId();
    int count = 0;
    for(int i = 0;i!=1000000;++i)
    {
        ++count;
    }
    emit SigMyThread(count);
    exec();
}

Controller

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include 
#include 
#include "mythread.h"

class Controller : public QObject
{
    Q_OBJECT
public:
    explicit Controller(QObject *parent = nullptr);
    ~Controller();
signals:
    void sigOperate( int para);
public slots:
    void onHandleResult( int result);
private:
   QThread m_WorkerThread;
   MyThread* m_pMyThread = nullptr;
};
#endif // CONTROLLER_H
#include "controller.h"
#include 
#include "worker.h"

Controller::Controller(QObject *parent)
    : QObject(parent)
{
#if 0
    Worker *worker = new Worker;
    worker->moveToThread(&m_pWorkerThread);            //调用moveToThread将该任务交给workThread

    connect(this, SIGNAL(sigOperate(int)), worker, SLOT(onDoWork(int)));            //operate信号发射后启动线程工作
    connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);            //该线程结束时销毁
    connect(worker, SIGNAL(sigResultReady(int)), this, SLOT(onHandleResult(int)));            //线程结束后发送信号,对结果进行处理

    m_pWorkerThread.start();                //启动线程
    qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId();
    emit sigOperate(0);
#endif
    m_pMyThread = new MyThread;
    connect(m_pMyThread,&MyThread::SigMyThread,this,&Controller::onHandleResult);
    connect(m_pMyThread, &QThread::finished, this, &QObject::deleteLater);            //该线程结束时销毁
    connect(this,&Controller::sigOperate,m_pMyThread,&MyThread::onMyThread);

    m_pMyThread->start();
    m_pMyThread->sleep(3);
    qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId();
//    QThread::sleep(5);   延迟5s 所有线程阻塞
    emit sigOperate(999);

}

Controller::~Controller()
{
   #if 0
    m_pWorkerThread.quit();
    m_pWorkerThread.wait();
#endif
    m_pMyThread->quit();
    m_pMyThread->wait();
}

void Controller::onHandleResult( int result)
{
    qDebug() << __FUNCTION__ <<"current ID :" <<QThread::currentThreadId();
    qDebug() << "the result is " <<result;
}

main.cpp

#include "mainwindow.h"
#include 
#include "controller.h"
#include 
#include 
#include 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
Controller controller;
    QTime dieTime = QTime::currentTime().addMSecs(1);
    while( QTime::currentTime() < dieTime)
    QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
    qDebug() << "main CurrentID :" << QThread::currentThreadId();
    return a.exec();
    }   

结果:

Qt多线程_第1张图片

moveToThread

说明

  1. 定义一个继承于QObject的worker类,在worker类中定义一个槽函数,这个函数中定义线程需要做的工作.
  2. 在要使用线程的controller类中,新建一个QThread的对象和woker类对象,使用moveToThread()方法将worker对象的事件循环全部交由QThread对象处理.
  3. 建立相关的信号函数和槽函数进行连接,然后发出信号触发QThread的槽函数,使其执行工作.

代码

worker

#ifndef WORKER_H
#define WORKER_H

#include 

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
    ~Worker();
signals:
    void sigResultReady(int result);
public slots:
    void onDoWork(int para);
};

#endif // WORKER_H
#include "worker.h"
#include 
#include 
Worker::Worker(QObject *parent)
    : QObject(parent)
{

}

Worker::~Worker()
{

}

void Worker::onDoWork(int para)
{
     qDebug()<<__FUNCTION__<< "current ID :" << QThread::currentThreadId();
    for(int i = 0; i != 1000000; ++i)
    {
        ++para;
    }
    emit sigResultReady(para);
}

Controller

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include 
#include 
#include "mythread.h"

class Controller : public QObject
{
    Q_OBJECT
public:
    explicit Controller(QObject *parent = nullptr);
    ~Controller();
signals:
    void sigOperate( int para);
public slots:
    void onHandleResult( int result);
private:
   QThread m_WorkerThread;
   MyThread* m_pMyThread = nullptr;
};

#endif // CONTROLLER_H
#include "controller.h"
#include 
#include "worker.h"

Controller::Controller(QObject *parent)
    : QObject(parent)
{
#if 1
    Worker *worker = new Worker;
    worker->moveToThread(&m_WorkerThread);            //调用moveToThread将该任务交给workThread

    connect(this, SIGNAL(sigOperate(int)), worker, SLOT(onDoWork(int)));            //operate信号发射后启动线程工作
    connect(&m_WorkerThread, &QThread::finished, worker, &QObject::deleteLater);            //该线程结束时销毁
    connect(worker, SIGNAL(sigResultReady(int)), this, SLOT(onHandleResult(int)));            //线程结束后发送信号,对结果进行处理

    m_WorkerThread.start();                //启动线程
    qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId();
    emit sigOperate(0);
#endif
    #if 0
    m_pMyThread = new MyThread;
    connect(m_pMyThread,&MyThread::SigMyThread,this,&Controller::onHandleResult);
    connect(m_pMyThread, &QThread::finished, this, &QObject::deleteLater);            //该线程结束时销毁
    connect(this,&Controller::sigOperate,m_pMyThread,&MyThread::onMyThread);

    m_pMyThread->start();
    m_pMyThread->sleep(3);
    qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId();
//    QThread::sleep(5);   延迟5s 所有线程阻塞
    emit sigOperate(999);
    #endif

}

Controller::~Controller()
{
   #if 0
    m_pWorkerThread.quit();
    m_pWorkerThread.wait();
#endif
    m_pMyThread->quit();
    m_pMyThread->wait();
}

void Controller::onHandleResult( int result)
{
    qDebug() << __FUNCTION__ <<"current ID :" <<QThread::currentThreadId();
    qDebug() << "the result is " <<result;
}

main

#include "mainwindow.h"
#include 
#include "controller.h"
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
#if 1
    Controller controller;
    qDebug() << "main CurrentID :" << QThread::currentThreadId();
#endif
   return a.exec();
}

结果:

Qt多线程_第2张图片

QtConcurrent::run()

说明

需要了解如下类 :

  1. QtConcurrent
  2. QFutureWatcher
  3. QFuture

QtConcurrent 是一个名字空间, 其内包含了众多的高级 API, 方便用户编写多线程程序.
QFutureWatcher 可以用于监视线程的完成情况, 并获取线程的返回值.
QtConcurrent 线程函数与 QFutureWatcher 之间的中间者是 QFuture.

代码

mainwindow

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void busyJob();
private:
    int doBusyJob();
    void busyJobFinished();
private:
    Ui::MainWindow *ui;
    QFutureWatcher* m_pWatch = nullptr;
};

#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include 
#include 
#include 
#include 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_pWatch = new QFutureWatcher<int>;
    connect(m_pWatch, &QFutureWatcher<int>::finished,
                this, &MainWindow::busyJobFinished);
}

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

void MainWindow::busyJob()
{
    qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId();
    auto future = QtConcurrent::run(this, &MainWindow::doBusyJob);
    m_pWatch->setFuture(future);
}

int MainWindow::doBusyJob()
{
     qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId();
     return 1;
}

void MainWindow::busyJobFinished()
{
    qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId();
    qDebug()<<__FUNCTION__<< "the returned value is: "
                 << m_pWatch->result();
}

main

#include "mainwindow.h"
#include 
#include "controller.h"
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
#if 1
    MainWindow w;
    qDebug()<<__FUNCTION__<<"current thread ID:"<

结果:

Qt多线程_第3张图片

方法比较

moveToThread方法,是把我们需要的工作全部封装在一个类中,将每个任务定义为一个的槽函数,再建立触发这些槽的信号,然后把信号和槽连接起来,最后将这个类调用moveToThread方法交给一个QThread对象,再调用QThread的start()函数使其全权处理事件循环。于是,任何时候我们需要让线程执行某个任务,只需要发出对应的信号就可以。其优点是我们可以在一个worker类中定义很多个需要做的工作,然后发出触发的信号线程就可以执行。相比于子类化的QThread只能执行run()函数中的任务,moveToThread的方法中一个线程可以做很多不同的工作(只要发出任务的对应的信号即可)。
子类化QThread的方法,就是重写了QThread中的run()函数,在run()函数中定义了需要的工作。这样的结果是,我们自定义的子线程调用start()函数后,便开始执行run()函数。如果在自定义的线程类中定义相关槽函数,那么这些槽函数不会由子类化的QThread自身事件循环所执行,而是由该子线程的拥有者所在线程(一般都是主线程)来执行。如果你不明白的话,请看,第二个例子中,子类化的线程的槽函数中输出当前线程的ID,而这个ID居然是主线程的ID!!事实的确是如此,子类化的QThread只能执行run()函数中的任务直到run()函数退出,而它的槽函数根本不会被自己的线程执行。

借鉴大神网址:
https://blog.csdn.net/qq_38410730/article/details/80783902
https://blog.csdn.net/ding_yingzi/article/details/80692099
主要是弄懂大神想法,自己动手敲代码弄清楚。如有侵权,请联系我更改。

你可能感兴趣的:(Qt,thread,Qt,多线程)