【QT】继承QRunnable+QThreadPool实现多线程
往期链接:
继承+实现多线程的方法个人感觉使用的相对较少,在这里只是简单介绍下使用的方法。我们可以根据使用的场景来选择方法。
此方法和的区别:
接下来就来看看的用法、使用场景以及注意事项;
一、步骤
要使用创建线程,步骤如下:
二、实例
继承于的类:
#ifndef INHERITQRUNNABLE_H
#define INHERITQRUNNABLE_H
#include
#include
#include
#include
class CusRunnable : public QRunnable
{
public:
explicit CusRunnable(){
}
~CusRunnable(){
qDebug() << __FUNCTION__;
}
void run(){
qDebug() << __FUNCTION__ << QThread::currentThreadId();
QThread::msleep(1000);
}
};
#endif // INHERITQRUNNABLE_H
主界面类:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include "ui_mainwindow.h"
#include "InheritQRunnable.h"
#include
#include
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0) :
QMainWindow(parent),
ui(new Ui::MainWindow){
ui->setupUi(this);
m_pRunnable = new CusRunnable();
qDebug() << __FUNCTION__ << QThread::currentThreadId();
QThreadPool::globalInstance()->start(m_pRunnable);
}
~MainWindow(){
qDebug() << __FUNCTION__ ;
delete ui;
}
private:
Ui::MainWindow *ui;
CusRunnable * m_pRunnable = nullptr;
};
#endif // MAINWINDOW_H
直接运行以上实例,结果输出如下:
MainWindow 0x377c
run 0x66ac
~CusRunnable
我们可以看到这里打印的线程ID是不同的,说明是在不同线程中执行,而线程执行完后就自动进入到析构函数中, 不需要手动释放。
三、启动线程的方式
上面我们说到要启动线程,需要配合使用,而调用方式有两种:全局线程池和非全局线程池。
(1)使用全局线程池启动
QThreadPool::globalInstance()->start(m_pRunnable);
(2)使用非全局线程池启动
该方式可以控制线程最大数量, 以及其他设置,比较灵活,具体参照帮助文档。
QThreadPool threadpool;
threadpool.setMaxThreadCount(1);
threadpool.start(m_pRunnable);
四、如何与外界通信
前面我们提到,因为没有继承于,所以没法使用信号槽与外界通信,那么,如果要在线程中和外界通信怎么办呢,通常有两种做法:
接下来只介绍使用::来通信:
:: 函数定义如下:
static bool QMetaObject::invokeMethod(
QObject *obj, const char *member,
Qt::ConnectionType,
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的函数,可以是信号、槽或者声明的函数(能够被Qt元对象系统唤起),只需要将函数的名称传递给此函数,调用成功返回true,失败返回false。函数调用的返回值放在ret中,如果调用是异步的,则不能计算返回值。你可以将最多10个参数(val0、val1、val2、val3、val4、val5、val6、val7、val8和val9)传递给函数,必须使用Q_ARG()和()宏封装参数,Q_ARG()接受类型名 + 该类型的常量引用;()接受一个类型名 + 一个非常量引用。
::可以是异步调用,也可以是同步调用。这取决与它的连接方式Qt:: type:
我们在主界面中定一个函数,用于更新界面内容:
Q_INVOKABLE void setText(QString msg){
ui->label->setText(msg);
}
继承于的线程类,修改完成如下:
#ifndef INHERITQRUNNABLE_H
#define INHERITQRUNNABLE_H
#include
#include
#include
#include
class CusRunnable : public QRunnable
{
public:
//修改构造函数
explicit CusRunnable(QObject *obj):m_pObj(obj){
}
~CusRunnable(){
qDebug() << __FUNCTION__;
}
void run(){
qDebug() << __FUNCTION__ << QThread::currentThreadId();
QMetaObject::invokeMethod(m_pObj,"setText",Q_ARG(QString,"hello world!")); //此处与外部通信
QThread::msleep(1000);
}
private:
QObject * m_pObj = nullptr; //定义指针
};
#endif // INHERITQRUNNABLE_H
创建线程对象时,需要将主界面对象传入线程类,如下:
m_pRunnable = new CusRunnable(this);
到这里也就实现了线程与外部通信了,运行效果如下:
五、小结
本文章实例的源码地址: