用法
要使用QRunnable创建线程,步骤如下:
- 继承QRunnable。和QThread使用一样,首先需要将你的线程类继承于QRunnable。
- 重写run函数。还是和QThread一样,需要重写run函数,run是一个纯虚函数,必须重写。
- 使用QThreadPool启动线程。
和QThread的区别
与外界通信方式不同。由于QThread是继承于QObject的,所以可以在线程类中使用信号槽,但QRunnable没有继承于QObject,所以不能使用信号与槽。
启动线程方式不同。QThread线程可以直接调用start()函数启动,而QRunnable线程需要借助QThreadPool进行启动。
资源管理不同。QThread线程对象需要手动去管理删除和释放,而QRunnable则会在QThreadPool调用完成后自动释放。
#ifndef CUSRUNNABLE_H
#define CUSRUNNABLE_H#include <QRunnable>class CusRunnable : public QRunnable
{
public:CusRunnable();~CusRunnable();void run() override;
};#endif // CUSRUNNABLE_H
#include "cusrunnable.h"
#include <QDebug>
#include <QThread>
CusRunnable::CusRunnable()
{}CusRunnable::~CusRunnable()
{qDebug()<<"~CusRunnable";
}void CusRunnable::run()
{for(int i=0;i<10;i++){qDebug()<<"doWork %d"<<i<<" thread_id"<<QThread::currentThreadId();QThread::sleep(1);}
}
CusRunnable* run=new CusRunnable();qDebug()<<"main thread_id="<<QThread::currentThreadId();QThreadPool::globalInstance()->start(run);
main thread_id= 0x7004
doWork %d 0 thread_id 0x6228
doWork %d 1 thread_id 0x6228
doWork %d 2 thread_id 0x6228
doWork %d 3 thread_id 0x6228
doWork %d 4 thread_id 0x6228
doWork %d 5 thread_id 0x6228
doWork %d 6 thread_id 0x6228
doWork %d 7 thread_id 0x6228
doWork %d 8 thread_id 0x6228
doWork %d 9 thread_id 0x6228
~CusRunnable
启动线程方式
上面说到启动QRunnable线程需要QThreadPool,而调用方式有两种:全局线程池和非全局线程池。
全局线程池如上面示例中。非全局线程池其实就是自己实例化一个QThreadPool对象,我们可以控制线程最大数量等。
CusRunnable* run=new CusRunnable();qDebug()<<"main thread_id="<<QThread::currentThreadId();QThreadPool pool;pool.start(run);
与外界通信
因为QRunnable没有继承于QObject,所以没法使用信号与槽。QRunnable与外界通信有两种方式:
- 使用多继承。让自定义的线程类同时继承于QRunnable和QObject,这样就可以使用信号与槽了,但是项目中尽量少用多继承。
- 使用QMetaObject::invokeMethod
QMetaObject::invokeMethod
函数定义如下:
[static] bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument( Q_NULLPTR ), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())
该函数就是尝试调用obj的member函数,可以是信号、槽或者Q_INVOKABLE声明的函数(能够被Qt元对象系统唤起),如果调用成功,返回true,失败返回false。
QMetaObject::invokeMethod可以是异步调用,也可以是同步调用。这取决与它的连接方式Qt::ConnectionType type。如果type为Qt::DirectConnection,则为同步调用,若为Qt::QueuedConnection,则为异步调用。
首先,我们在使用CusRunnable类的类中定义如下函数:
Q_INVOKABLE void onResultReady(const QString result);void SIHToolBar::onResultReady(const QString result)
{qDebug()<<"onResultReady result="<<result<<" thread_id="<<QThread::currentThreadId();
}
然后,我们想要在CusRunnable类中调用这个函数,就需要这个这个函数的对象。所以,我们实例化CusRunnable的时候,把该对象传进去。
CusRunnable(QObject* obj);
最后,我们在run函数中调用这个对象的函数。
void CusRunnable::run()
{for(int i=0;i<10;i++){qDebug()<<"doWork %d"<<i<<" thread_id"<<QThread::currentThreadId();QThread::sleep(1);}QMetaObject::invokeMethod(obj,"onResultReady",Q_ARG(QString,"hello"));
}