Qt 多线程使用形式

发布时间 2023-04-18 17:38:20作者: 卑以自牧lq

Qt 多线程 QThread、QThreadPool使用场景

QThread

优点:

  可以使用信号槽进行通信

缺点:

  需要自己管理资源,线程的创建和释放,都需要自己手动管理,并且,频繁的创建和删除会造成比较大的内存开销。

适用场景:

  线程不会被频繁的创建和删除,常驻内存的线程。

QThread 有两种使用方式:

  1. 通过moveToThread()实现 (qt开发者更推荐的方式)
  2. 通过子类继承QThread实现

moveToThread方式

#include <iostream>
#include <QThread>
#include <QDebug>
using namespace std;

class worker : public QObject {
    Q_OBJECT
public slots:
    void doWork(const QString &parameter) {
        qDebug() << "work Thread ID:" << QThread::currentThreadId();
        QString result;
        emit resultReady(result);
    }

signals:
    void resultReady(const QString& result);
};

class controller : public QObject {
    Q_OBJECT
public:
    controller() {
        worker* w = new worker();
        w->moveToThread(&_thread);
        connect(&_thread, &QThread::finished, w, &QObject::deleteLater);
        connect(this, &controller::begin, w, &worker::doWork);
        connect(w, &worker::resultReady, this, &controller::handleResults);
        _thread.start();
    }
    ~controller() {
        _thread.quit();
        _thread.wait();
    }

public slots:
    void handleResults(const QString& result) { cout << "I'm handleResult" << endl; }

signals:
    void begin(const QString& parameter);

private:
    QThread     _thread;
};

/************测试**************/
#include <QCoreApplication>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    qDebug() << "main Thread ID: " << QThread::currentThreadId();
    controller c;
    emit c.begin("");

    return app.exec();
}
//输出
main Thread ID:  0x6848
work Thread ID: 0x5724
I'm handleResult

子线程必须在 event loop 里执行,因为子线程是由主线程创建的,如果主线程退出了,子线程会也会立即退出。不管是否执行完。

子类继承QThread方式

#include <iostream>
#include <QThread>
#include <QDebug>
using namespace std;

class myThread : public QThread {
    Q_OBJECT
public:
    myThread() {
        connect(this, &myThread::finished, this, &myThread::deleteLater);
    }
    void run() override {
        QString result;
        qDebug() << "sub Thread ID:" << QThread::currentThreadId();
        // do something
        emit resultReady(result);
    }

signals:
    void resultReady(const QString&);
};

class myObject : public QObject {
    Q_OBJECT
public:
    myObject() {
        myThread *mt = new myThread();
        connect(mt, &myThread::resultReady, this, &myObject::handleResult);
        mt->start();
    }
public slots:
    void handleResult(const QString& result) { cout << "I'm handleResult" << endl; }
};

/**********测试******************/
#include <QCoreApplication>

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

    qDebug() << "main Thread ID: " << QThread::currentThreadId();
    myObject mo;

    return app.exec();
}

//输出
main Thread ID:  0x639c
sub Thread ID: 0x75ec
I'm handleResult

注意:
  实例化的子类是在创建线程的旧线程中,不是在新创建的子线程中,该线程的槽函数还是在主线程执行,所以, 不能直接在新建的线程中使用槽。要想在子线程执行槽函数,只能用moveToThread方式
  实例化子类的构造函数和run()函数在不同的线程中运行,因此,假设有成员变量两个函数中都能访问,则需要注意,多线程中资源的访问问题。

QThreadPool与QRunnable

优点:

不用资源管理,QThreadPool 启动线程执行完成后会自动释放

缺点:

可能会形成多线程资源竞争
不能使用信号槽(信号槽只能在QObject中使用)

适用场景:

QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销。

QtConcurrent

QtConcurrent是一个命名空间,提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。这意味着今后编写的应用程序将在未来部署在多核系统上时继续扩展。
QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单。

QtConcurrent 的底层实现是 QThreadPool与QRunnable