概述

Qt中有多种创建线程的方式,每一种的应用场景和使用方式都有些区别, 这里主要简单介绍Qt里面的几大创建线程的方法,以及使用注意事项。

QThread

使用QThread创建线程是我们最常见的一种方式,步骤如下:

  • 继承QThread
  • 重写run()函数
  • 通过start()函数启动线程

优点:可以通过信号槽与外界进行通信。
缺点:①每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。
②要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。
适用场景:QThread适用于那些常驻内存的任务。

#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
Q_OBJECT
protected:
void run(){
//do something
qDebug() <<__FUNCTION__ << "id = " << QThread::currentThreadId();
QThread::msleep(1000);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug() <<__FUNCTION__ << "id = " << QThread::currentThreadId();

MyThread * myThread = new MyThread;
myThread->start();

return a.exec();
}

输出:

main id =  0x29a4
MyThread::run id = 0x1c2c

QRunnable

前面一篇文章已经详细介绍了QRunnable的使用方法​​【文章在这里】​​。 所以这里就不详细描述了, 重点讲一下优缺点和适用场景。

步骤如下:

  • 继承QRunnable。和QThread使用一样, 首先需要将你的线程类继承于QRunnable。
  • 重写run函数。还是和QThread一样,需要重写run函数,run是一个纯虚函数,必须重写。
  • 使用QThreadPool启动线程

优点:无需手动释放资源,QThreadPool启动线程执行完成后会自动释放。
缺点:不能使用信号槽与外界通信。
适用场景:QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销。

具体代码就不贴了,详情参见上一篇文章。

moveToThread

moveToThread是在QThread的用法基础上扩展出来的,它是QObject的接口,相对QThread线程方式来说,moveToThread使用更灵活,不需要继承QThread,也不用重写run函数。只需要将一个继承于QObject的类通过moveToThread移到QThread的一个对象中。
需要注意的是:

  • 只有在槽中执行的操作才是在线程中执行的,所以需要通过连接信号槽的方式来实现
  • 如果object对象存在父对象,不能将其移到子线程中执行。

示例如下:

#include <QObject>
#include <QThread>

class Command : public QObject
{
Q_OBJECT
public:
explicit Command(QObject *parent = nullptr);
void sendMessage(const QString &msg);

signals:
void sigMsg(QString msg);

private slots:
void onMessage(QString msg);

private:
QThread * m_pThread = nullptr;
};
#include "command.h"
#include <QDebug>

Command::Command(QObject *parent) :
QObject(parent)
{
m_pThread = new QThread();
this->moveToThread(m_pThread);
connect(this,&Command::sigMsg,this,&Command::onMessage);
m_pThread->start();
qDebug()<< __FUNCTION__ << " id = "<< QThread::currentThreadId();
}

void Command::sendMessage(const QString &msg)
{
emit sigMsg(msg);
}

void Command::onMessage(QString msg)
{
qDebug()<< __FUNCTION__ << " id = "<< QThread::currentThreadId();
}

调用:

m_pCommand = new Command();   //不能指定父类
m_pCommand->sendMessage("ABC");

输出:

Command::Command  id =  0x4b58
Command::onMessage id = 0x3c84

QtConcurrent::run

有关QtConcurrent::run的使用方法在之前的文章里面有详细介绍过,​​在这里​​,Concurrent是并发的意思,QtConcurrent是一个命名空间,提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。这意味着今后编写的应用程序将在未来部署在多核系统上时继续扩展。

QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单。详见前面的文章介绍,这里不再赘述。

需要注意的是,由于该线程取自全局线程池QThreadPool,函数不能立马执行,需要等待线程可用时才会运行。