大家好,欢迎来到IT知识分享网。
为何需要多线程?
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; /* ... here is the expensive or blocking operation ... */ 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; /* ... here is the expensive or blocking operation ... */ 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类中槽函数连接的信号,槽函数依次执行。
领Qt学习资料→「链接」
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(); }
运行结果:

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/182932.html