0
点赞
收藏
分享

微信扫一扫

Qt-QThread

E_topia 2022-05-01 阅读 47

概述


       QThread类提供了一个与平台无关的管理线程的方法。一个QThread对象管理一个线程。QThread的执行从run()函数的执行开始,在Qt自带的QThread类中,run()函数通过调用exec()函数来启动事件循环机制,并且在线程内部处理Qt的事件。在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,从而让主界面能及时响应用户的请求操作。

QThread的使用方法有如下两种:

  • QObject::moveToThread()
  • 继承QThread类

QObject::moveToThread()

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

       首先新建一个work类,该类重点在于其doWork槽函数,这个函数定义了线程需要做的工作,需要向其发送信号来触发。Wrok类的头文件中定义了全部函数.

Work.h

// work定义了线程要执行的工作
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include<QDebug>
#include<QThread>

class Worker :public QObject
{
	Q_OBJECT
public:
	Worker(QObject* parent = nullptr) {}
public slots:
	// doWork定义了线程要执行的操作
	void doWork(int parameter)
	{
		qDebug() << "receive the execute signal---------------------------------";
		qDebug() << "     current thread ID:" << QThread::currentThreadId();

		++parameter;
		// 发送结束信号
		emit resultReady(parameter);
	}

	// 线程完成工作时发送的信号
signals:
	void resultReady(const int result);
};

#endif // WORKER_H


Controller.h

#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include<QThread>
#include<QDebug>
#include "Worker.h"

// controller用于启动线程和处理线程执行结果
class Controller : public QObject
{
	Q_OBJECT
		
public:
	Controller(QObject *parent = nullptr)
	{
		Worker *worker = new Worker;
		//调用moveToThread将该任务交给workThread
		worker->moveToThread(&workerThread);
		//operate信号发射后启动线程工作
		connect(this, SIGNAL(operate(const int)), worker, SLOT(doWork(int)));
		//该线程结束时销毁
		connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
		//线程结束后发送信号,对结果进行处理
		connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));
		//启动线程
		workerThread.start();
		//发射信号,开始执行
		qDebug() << "emit the signal to execute!---------------------------------";
		qDebug() << "     current thread ID:" << QThread::currentThreadId() << '\n';
		emit operate(0);
	}

	~Controller() {
		workerThread.quit();
		workerThread.wait();
	}

public slots:
	// 处理线程执行的结果
	void handleResults(const int rslt)
	{
		qDebug() << "receive the resultReady signal---------------------------------";
		qDebug() << "     current thread ID:" << QThread::currentThreadId() << '\n';
		qDebug() << "     the last result is:" << rslt;
	}
signals:
	// 发送信号触发线程
	void operate(const int);
private:
	QThread workerThread;
};

#endif // CONTROLLER_H

在需要使用的地方定义一个Controller c;

结果:

 

继承QThread的方法

方法描述

  1. 自定义一个继承QThread的类MyThread,重载MyThread中的run()函数,在run()函数中写入需要执行的工作.
  2. 调用start()函数来启动线程。

继承QThread的例子
       首先写MyThread类,该类继承于QThread,该类中自定义了信号槽和重写了run函数。头文件如下:

MyThread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
#include<QDebug>
class MyThread : public QThread
{
	Q_OBJECT
public:
	MyThread(QObject* parent = nullptr);
	//自定义发送的信号
signals:
	void myThreadSignal(int);
	//自定义槽
public slots:
	void myThreadSlot(int);
protected:
	void run() override;
};

#endif // MYTHREAD_H

MyThread.cpp

#include "mythread.h"

MyThread::MyThread(QObject *parent)
{

}

void MyThread::run()
{
	qDebug() << "myThread run() start to execute";
	qDebug() << "     current thread ID:" << QThread::currentThreadId() << '\n';

	int count = 0;
	++count;

	// 发送结束信号
	emit myThreadSignal(count);
	exec();
}

void MyThread::myThreadSlot(int val)
{
	qDebug() << "myThreadSlot() start to execute";
	qDebug() << "     current thread ID:" << QThread::currentThreadId() << '\n';
	// 
	int count = 0;
	++count;
}

Controller.h

#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include<QThread>
#include<QDebug>
#include "MyThread.h"

// controller用于启动线程和处理线程执行结果
class Controller : public QObject
{
	Q_OBJECT
		
public:
	Controller(QObject *parent = nullptr)
	{
		myThrd = new MyThread;
		connect(myThrd, &MyThread::myThreadSignal, this, &Controller::handleResults);
		// 该线程结束时销毁
		connect(myThrd, &QThread::finished, this, &QObject::deleteLater);
		connect(this, &Controller::operate, myThrd, &MyThread::myThreadSlot);

		// 启动该线程
		myThrd->start();
		QThread::sleep(1);
		emit operate(999);
	}

	~Controller() {
		myThrd->quit();
		myThrd->wait();
	}

public slots:
	// 处理线程执行的结果
	void handleResults(int rslt)
	{
		qDebug() << "receive the resultReady signal---------------------------------";
		qDebug() << "     current thread ID:" << QThread::currentThreadId() << '\n';
		qDebug() << "     the last result is:" << rslt;
	}
signals:
	// 发送信号触发线程
	void operate( int);
private:
	MyThread* myThrd;
};

#endif // CONTROLLER_H

在需要执行的地方定义  Controller c;

 

        通过自定义一个继承QThread的类,实例化该类的对象,重载run()函数为需要做的工作。然后在需要的地方调用start函数来执行run函数中的任务。从主函数触发了一个信号,对应于子线程的槽,子线程的槽函数中打印当前执行的线程的编号,可以看到,执行子线程的槽函数的线程编号却是主线程的编号。

两种方法的比较

        moveToThread方法,是把我们需要的工作全部封装在一个类中,将每个任务定义为一个的槽函数,再建立触发这些槽的信号,然后把信号和槽连接起来,最后将这个类调用moveToThread方法交给一个QThread对象,再调用QThread的start()函数使其全权处理事件循环。于是,任何时候我们需要让线程执行某个任务,只需要发出对应的信号就可以。其优点是我们可以在一个worker类中定义很多个需要做的工作,然后发出触发的信号线程就可以执行。相比于子类化的QThread只能执行run()函数中的任务,moveToThread的方法中一个线程可以做很多不同的工作(只要发出任务的对应的信号即可)。

        子类化QThread的方法,就是重写了QThread中的run()函数,在run()函数中定义了需要的工作。这样的结果是,我们自定义的子线程调用start()函数后,便开始执行run()函数。如果在自定义的线程类中定义相关槽函数,那么这些槽函数不会由子类化的QThread自身事件循环所执行,而是由该子线程的拥有者所在线程(一般都是主线程)来执行

源码分析

class Q_CORE_EXPORT QThread : public QObject
{
    Q_OBJECT
public:
    static Qt::HANDLE currentThreadId() Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION;
    static QThread *currentThread();
    static int idealThreadCount() Q_DECL_NOTHROW;
    static void yieldCurrentThread();

    explicit QThread(QObject *parent = Q_NULLPTR);
    ~QThread();

    //调度级别
    enum Priority {
        IdlePriority,

        LowestPriority,
        LowPriority,
        NormalPriority,
        HighPriority,
        HighestPriority,

        TimeCriticalPriority,

        InheritPriority
    };

    void setPriority(Priority priority);
    Priority priority() const;

    bool isFinished() const;
    bool isRunning() const;

    void requestInterruption();
    bool isInterruptionRequested() const;

    void setStackSize(uint stackSize);
    uint stackSize() const;

    void exit(int retcode = 0);

    QAbstractEventDispatcher *eventDispatcher() const;
    void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);

    bool event(QEvent *event) Q_DECL_OVERRIDE;
    int loopLevel() const;

public Q_SLOTS:
    void start(Priority = InheritPriority);//开始函数
    void terminate();
    void quit();

public:
    // default argument causes thread to block indefinetely
    bool wait(unsigned long time = ULONG_MAX);

    static void sleep(unsigned long);
    static void msleep(unsigned long);
    static void usleep(unsigned long);

Q_SIGNALS:
    void started(QPrivateSignal);
    void finished(QPrivateSignal);

protected:
    virtual void run();
    int exec();

    static void setTerminationEnabled(bool enabled = true);

protected:
    QThread(QThreadPrivate &dd, QObject *parent = Q_NULLPTR);

private:
    Q_DECLARE_PRIVATE(QThread)

    friend class QCoreApplication;
    friend class QThreadData;
};

C:\Qt\5.9.8\Src\qtbase\src\corelib\thread\qthread_win.cpp

start

void QThread::start(Priority priority)
{
    Q_D(QThread);
    QMutexLocker locker(&d->mutex);

    if (d->isInFinish) {
        locker.unlock();
        wait();
        locker.relock();
    }

    if (d->running)
        return;

    d->running = true;
    d->finished = false;
    d->exited = false;
    d->returnCode = 0;
    d->interruptionRequested = false;

......

    // MSVC -MT or -MTd build
    d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
                                            this, CREATE_SUSPENDED, &(d->id));
#else
    // MSVC -MD or -MDd or MinGW build  调用windows原生接口创建线程,线程中的函数指针指向QThreadPrivate::start
    d->handle = (Qt::HANDLE) CreateThread(NULL, d->stackSize, (LPTHREAD_START_ROUTINE)QThreadPrivate::start,
                                            this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
#endif // Q_OS_WINRT

......
}

创建线程中的函数指针 QThreadPrivate::start


unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg)
{
    QThread *thr = reinterpret_cast<QThread *>(arg);
    QThreadData *data = QThreadData::get2(thr);


    //1 创建线程的局部变量
    qt_create_tls();
    TlsSetValue(qt_current_thread_data_tls_index, data);
    data->threadId.store(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));

    QThread::setTerminationEnabled(false);

    {
        QMutexLocker locker(&thr->d_func()->mutex);
        data->quitNow = thr->d_func()->exited;
    }

    //2 创建事件分发器
    if (data->eventDispatcher.load()) // custom event dispatcher set?
        data->eventDispatcher.load()->startingUp();
    else
        createEventDispatcher(data);

    //3 设置线程名称
#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINRT)
    // sets the name of the current thread.
    QByteArray objectName = thr->objectName().toLocal8Bit();
    qt_set_thread_name((HANDLE)-1,
                       objectName.isEmpty() ?
                       thr->metaObject()->className() : objectName.constData());
#endif

    //4 发送当前线程启动信号
    emit thr->started(QThread::QPrivateSignal());
    QThread::setTerminationEnabled(true);

    //5 调用QThread run函数
    thr->run();

    finish(arg);
    return 0;
}

void QThread::run()
    run(): 线程的起点。 调用 start() 后,新创建的线程调用此函数。 默认实现只是调用 exec()。您可以重新实现此功能以方便高级线程管理。 

    exec():进入事件循环,等待 exit() 被调用,返回值传递给 exit()。此函数旨在从 run() 中调用。 有必要 调用此函数开始事件处理。

void QThread::run()
{
    (void) exec();
}

int QThread::exec()
{
    Q_D(QThread);
    QMutexLocker locker(&d->mutex);
    d->data->quitNow = false;
    if (d->exited) {
        d->exited = false;
        return d->returnCode;
    }
    locker.unlock();

    //启动事件循环器
    QEventLoop eventLoop;
    int returnCode = eventLoop.exec();

    locker.relock();
    d->exited = false;
    d->returnCode = -1;
    return returnCode;
}

        总结:当调用 QThread::start()接口时,在windows平台会使用原生api创建线程,并且函数指针是QThreadPrivate::start,在QThreadPrivate::start中会调用Thread::run函数,Thread::run调用exec()函数启动事件循环器eventLoop;

        每一个QThread start都会默认开启一个事件循环eventLoop。

exit


     告诉线程的事件循环以返回码退出。 调用此函数后,线程离开事件循环并从对 QEventLoop::exec() 的调用返回。

void QThread::exit(int returnCode)
{
    Q_D(QThread);
    QMutexLocker locker(&d->mutex);
    d->exited = true;
    d->returnCode = returnCode;
    d->data->quitNow = true;
    for (int i = 0; i < d->data->eventLoops.size(); ++i) {
        QEventLoop *eventLoop = d->data->eventLoops.at(i);
        eventLoop->exit(returnCode);
    }
}
举报

相关推荐

0 条评论