为何需要多线程?
1、进行耗时操作时,可以处理用户的其他输入输出。比如,如果在UI线程里面进行耗时操作,界面会不响应用户操作。
2、提升程序性能。现在的电脑一般都是多核CPU,多线程并行处理事务,可以大大提升程序的性能。
针对第一点,为我们定位界面不响应问题指明了一个方向;针对第二点,为我们提升软件处理效率指明了一个方向。
那么,基于Qt开发的应用程序,如何实现多线程呢?
目录
1、继承QThread,重载run函数。
2、继承QObject,调用void QObject::moveToThread(QThread *targetThread)。
3、QThreadPool and QRunnabl。
4、Qt Concurrent。
5、测试代码
使用多线程之前,特别需要注意的一点是:
非UI线程不能操作UI对象(从QWidget直接或间接派生的窗口对象)
1、继承QThread,重载run函数。
这种方法比较适用于处理耗时很长的业务。示例代码如下:
class WorkerThread : public QThread { Q_OBJECT void run() override { QString result; 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(); }需要注意以下几点:
1)run函数在新线程中执行,run函数执行结束,线程结束。
2)WorkerThread实例化的对象属于创建他的线程,而不是run函数所在线程。
3)WorkerThread没有事件循环,除非在run()函数中调用exec();
4)队列连接到WorkerThread的slot函数,slot函数在创建WorkerThread对象的线程中执行。
5)直接调用WorkerThread的方法,该方法的执行线程为调用处的线程。
2、继承QObject,调用void QObject::moveToThread(QThread *targetThread)。
这种方法适用于在一个类中处理多个耗时任务,且这个些任务不会并行执行的情况。示例代码如下:
class Worker : public QObject { Q_OBJECT public slots: void doWork(const QString ¶meter) { QString result; 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 &); };需要注意以下几点:
1)调用moveToThread函数的对象不能设置父对象。
2)Worker类中的槽函数可以跟任意线程的任意信号建立连接,队列连接时,在新线程中执行。
3)直接调用Worker类中的函数,在调用线程内执行。
4)同时发送多个与Worker类中槽函数连接的信号,槽函数依次执行。
3、QThreadPool and QRunnabl。
我们都知道频繁创建和销毁线程会带来较大的性能开销,影响程序执行效率。Qt的线程池技术,给了我们一个解决这个问题的有效方法。示例代码如下:
class HelloWorldTask : public QRunnable { void run() override { qDebug() << "Hello world from thread" << QThread::currentThread(); } }; HelloWorldTask *hello = new HelloWorldTask(); // QThreadPool takes ownership and deletes 'hello' automatically QThreadPool::globalInstance()->start(hello);注意:
1)默认情况下,run函数执行完,hello对象会被线程池自动删除。可以使用setAutoDelete函数设置。
2)QThreadPool::start()多次启动设置为autoDelete的QRunnable对象,可能导致崩溃。
4、Qt Concurrent。
QtConcurrent提供了高级api,使编写多线程程序时,不需要使用诸如互斥锁、读写锁、等待条件或信号量等低级线程安全类。具体用法,我们下回分解。传送门:Qt Concurrent 线程使用详解
Concurrent Run
extern void aFunction(); QFuture<void> future = QtConcurrent::run(aFunction); Concurrent Map and Map-Reduce
QList<QImage> images = ...; // Each call blocks until the entire operation is finished. QList<QImage> future = QtConcurrent::blockingMapped(images, scaled); QtConcurrent::blockingMap(images, scale); QImage collage = QtConcurrent::blockingMappedReduced(images, scaled, addToCollage);Concurrent Filter and Filter-Reduce
QStringList strings = ...; // each call blocks until the entire operation is finished QStringList lowerCaseStrings = QtConcurrent::blockingFiltered(strings, allLowerCase); QtConcurrent::blockingFilter(strings, allLowerCase); QSet<QString> dictionary = QtConcurrent::blockingFilteredReduced(strings, allLowerCase, addToDictionary);5、测试代码
main.cpp
#include "qtthreaddemo.h"#include <QtWidgets/QApplication>#include <QDebug>#include <QThread> int main(int argc, char *argv[]){ QApplication a(argc, argv); qDebug() << "Main Thread Id:" << QThread::currentThreadId(); QtThreadDemo w; w.show(); return a.exec();}qtthreaddemo.h
#pragma once #include <QtWidgets/QWidget>#include "ui_qtthreaddemo.h" class WorkerThread1;class WorkerThread2; class QtThreadDemo : public QWidget{ Q_OBJECT public: QtThreadDemo(QWidget *parent = Q_NULLPTR);private slots: void on_btnCreateThread1_clicked(); void on_btnSlotFun_clicked(); void on_btnCreateThread2_clicked(); void on_btnTestOrders_clicked(); void on_btnRunnableThread_clicked(); void workerThreadEnd();signals: void run_slot();private: Ui::QtThreadDemoClass ui; WorkerThread1 *m_pThread1 = nullptr; WorkerThread2 *m_pThread2 = nullptr;};qtthreaddemo.cpp
#include "qtthreaddemo.h"#include "workerthread1.h"#include "workerthread2.h"#include "runnablethread.h"#include <QDebug>#include <QThreadPool> QtThreadDemo::QtThreadDemo(QWidget *parent) : QWidget(parent){ ui.setupUi(this);} void QtThreadDemo::on_btnCreateThread1_clicked(){ qDebug() << "btnCreateThread clicked Thread Id:" << QThread::currentThreadId(); m_pThread1 = new WorkerThread1(this); connect(this, SIGNAL(run_slot()), m_pThread1, SLOT(doJob()), Qt::QueuedConnection); connect(m_pThread1, SIGNAL(finished()), this, SLOT(workerThreadEnd())); m_pThread1->start();} void QtThreadDemo::on_btnSlotFun_clicked(){ qDebug() << "btnSlotFun clicked Thread Id:" << QThread::currentThreadId(); emit run_slot();} void QtThreadDemo::on_btnCreateThread2_clicked(){ qDebug() << "btnCreateThread2 clicked"; if (m_pThread2 == nullptr) { m_pThread2 = new WorkerThread2(nullptr); } m_pThread2->doJob();} void QtThreadDemo::on_btnTestOrders_clicked(){ qDebug() << "btnTestOrders clicked"; if (m_pThread2 == nullptr) { m_pThread2 = new WorkerThread2(nullptr); } m_pThread2->testOrder();} void QtThreadDemo::on_btnRunnableThread_clicked(){ qDebug() << "btnRunnableThread clicked"; RunnableThread *pThread = new RunnableThread; QThreadPool::globalInstance()->tryStart(pThread); QThread::msleep(2000); //QThreadPool::globalInstance()->tryStart(pThread); //会发生异常} void QtThreadDemo::workerThreadEnd(){ qDebug() << "Worker Thread Ended!";}workthread1.h
#pragma once #include <QThread> class WorkerThread1 : public QThread{ Q_OBJECT public: WorkerThread1(QObject *parent); ~WorkerThread1(); static void publicFun();protected: void run(); private slots: void doJob();};workerthread1.cpp
#include "workerthread1.h"#include <QDebug> WorkerThread1::WorkerThread1(QObject *parent) : QThread(parent){ qDebug() << "WorkerThread1 Object Thread Id:" << QThread::currentThreadId();} WorkerThread1::~WorkerThread1(){} void WorkerThread1::publicFun(){ qDebug() << "WorkerThread1::publicFun Thread Id:" << QThread::currentThreadId();} void WorkerThread1::run(){ qDebug() << "WorkerThread1 Create Thread Id:" << QThread::currentThreadId();} void WorkerThread1::doJob(){ qDebug() << "WorkerThread1 Slot-doJob Thread Id:" << QThread::currentThreadId();}workerthread2.h
#pragma once #include <QObject>class QThread; class WorkerThread2 : public QObject{ Q_OBJECT public: WorkerThread2(QObject *parent); ~WorkerThread2(); void doJob(); void testOrder(); private slots: void onDoJob(); void onOrder1(); void onOrder2(); void onOrder3(); signals: void sig_do_job(); void sig_order1(); void sig_order2(); void sig_order3();private: QThread *m_pThread;};workerthread2.cpp
#include "workerthread2.h"#include <QThread>#include <QDebug>#include "workerthread1.h" WorkerThread2::WorkerThread2(QObject *parent) : QObject(parent){ m_pThread = new QThread; moveToThread(m_pThread); m_pThread->start(); connect(this, SIGNAL(sig_do_job()), this, SLOT(onDoJob()), Qt::QueuedConnection); connect(this, SIGNAL(sig_order1()), this, SLOT(onOrder1()), Qt::QueuedConnection); connect(this, SIGNAL(sig_order2()), this, SLOT(onOrder2()), Qt::QueuedConnection); connect(this, SIGNAL(sig_order3()), this, SLOT(onOrder3()), Qt::QueuedConnection);} WorkerThread2::~WorkerThread2(){} void WorkerThread2::doJob(){ qDebug() << "WorkerThread2::doJob thread id:" << QThread::currentThreadId(); emit sig_do_job();} void WorkerThread2::testOrder(){ emit sig_order1(); emit sig_order2(); emit sig_order3();} void WorkerThread2::onDoJob(){ qDebug() << "WorkerThread2::onDoJob thread id:" << QThread::currentThreadId(); WorkerThread1::publicFun();} void WorkerThread2::onOrder1(){ qDebug() << "WorkerThread2::onOrder1 begin"; QThread::msleep(1000); qDebug() << "WorkerThread2::onOrder1 end";} void WorkerThread2::onOrder2(){ qDebug() << "WorkerThread2::onOrder2 begin"; QThread::msleep(1000); qDebug() << "WorkerThread2::onOrder2 end";} void WorkerThread2::onOrder3(){ qDebug() << "WorkerThread2::onOrder3 begin"; QThread::msleep(1000); qDebug() << "WorkerThread2::onOrder3 end";}runnablethread.h
#pragma once #include <QRunnable> class RunnableThread : public QRunnable{public: RunnableThread(); ~RunnableThread(); void run();};runnablethread.cpp
#include "runnablethread.h"#include <QDebug>#include <QThread> RunnableThread::RunnableThread(){} RunnableThread::~RunnableThread(){} void RunnableThread::run(){ qDebug() << "RunnableThread::run Thread Id:" << QThread::currentThreadId();}运行结果:

