往期链接:

继承QRunnable+QThreadPool实现多线程的方法个人感觉使用的相对较少,在这里只是简单介绍下使用的方法。我们可以根据使用的场景来选择方法。

此方法和QThread的区别:

  • 与外界通信方式不同。由于QThread是继承于QObject的,但QRunnable不是,所以在QThread线程中,可以直接将线程中执行的结果通过信号的方式发到主程序,而QRunnable线程不能用信号槽,只能通过别的方式,等下会介绍;
  • 启动线程方式不同。QThread线程可以直接调用start()函数启动,而QRunnable线程需要借助QThreadPool进行启动;
  • 资源管理不同。QThread线程对象需要手动去管理删除和释放,而QRunnable则会在QThreadPool调用完成后自动释放。

接下来就来看看QRunnable的用法、使用场景以及注意事项;

一、步骤

要使用QRunnable创建线程,步骤如下:

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

二、实例

继承于QRunnable的类:

#ifndef INHERITQRUNNABLE_H
#define INHERITQRUNNABLE_H #include <QRunnable>
#include <QWidget>
#include <QDebug>
#include <QThread> 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 <QMainWindow>
#include "ui_mainwindow.h"
#include "InheritQRunnable.h"
#include <QThreadPool>
#include <QDebug> 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是不同的,说明是在不同线程中执行,而线程执行完后就自动进入到析构函数中, 不需要手动释放。

三、启动线程的方式

上面我们说到要启动QRunnable线程,需要QThreadPool配合使用,而调用方式有两种:全局线程池和非全局线程池。

(1)使用全局线程池启动

QThreadPool::globalInstance()->start(m_pRunnable);

(2)使用非全局线程池启动

该方式可以控制线程最大数量, 以及其他设置,比较灵活,具体参照帮助文档。

QThreadPool	  threadpool;
threadpool.setMaxThreadCount(1);
threadpool.start(m_pRunnable);

四、如何与外界通信

前面我们提到,因为QRunnable没有继承于QObject,所以没法使用信号槽与外界通信,那么,如果要在QRunnable线程中和外界通信怎么办呢,通常有两种做法:

  • 使用多继承。让我们的自定义线程类同时继承于QRunnable和QObject,这样就可以使用信号和槽,但是多线程使用比较麻烦,特别是继承于自定义的类时,容易出现接口混乱,所以在项目中尽量少用多继承。
  • 使用QMetaObject::invokeMethod。

接下来只介绍使用QMetaObject::invokeMethod来通信:

QMetaObject::invokeMethod 函数定义如下:

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的member函数,可以是信号、槽或者Q_INVOKABLE声明的函数(能够被Qt元对象系统唤起),只需要将函数的名称传递给此函数,调用成功返回true,失败返回false。member函数调用的返回值放在ret中,如果调用是异步的,则不能计算返回值。你可以将最多10个参数(val0、val1、val2、val3、val4、val5、val6、val7、val8和val9)传递给member函数,必须使用Q_ARG()和Q_RETURN_ARG()宏封装参数,Q_ARG()接受类型名 + 该类型的常量引用;Q_RETURN_ARG()接受一个类型名 + 一个非常量引用。

QMetaObject::invokeMethod可以是异步调用,也可以是同步调用。这取决与它的连接方式Qt::ConnectionType type:

  • 如果类型是Qt::DirectConnection,则会立即调用该成员,同步调用。
  • 如果类型是Qt::QueuedConnection,当应用程序进入主事件循环时,将发送一个QEvent并调用该成员,异步调用。
  • 如果类型是Qt::BlockingQueuedConnection,该方法将以与Qt::QueuedConnection相同的方式调用,不同的地方:当前线程将阻塞,直到事件被传递。使用此连接类型在同一线程中的对象之间通信将导致死锁。
  • 如果类型是Qt::AutoConnection,如果obj与调用者在同一线程,成员被同步调用;否则,它将异步调用该成员。

我们在主界面中定一个函数,用于更新界面内容:

Q_INVOKABLE void setText(QString msg){
ui->label->setText(msg);
}

继承于QRunnable的线程类,修改完成如下:

#ifndef INHERITQRUNNABLE_H
#define INHERITQRUNNABLE_H #include <QRunnable>
#include <QWidget>
#include <QDebug>
#include <QThread> 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);

到这里也就实现了线程与外部通信了,运行效果如下:

五、小结

  • 使用该方法实现的多线程,线程中的资源无需用户手动释放,线程执行完后会自动回收资源;
  • 和继承QThread的方法一样需要继承类,并且重新实现run函数;
  • 需要结合QThreadPool线程池来使用;
  • 与外界通信可以使用如果使用信号槽机制会比较麻烦,可以使用QMetaObject::invokeMethod的方式与外界通信。

本文章实例的源码地址:https://gitee.com/CogenCG/QThreadExample.git

【QT】继承QRunnable+QThreadPool实现多线程的更多相关文章

  1. 【QT】QtConcurrent::run()+QThreadPool实现多线程

    往期链接: <QThread源码浅析> <子类化QThread实现多线程> <子类化QObject+moveToThread实现多线程> <继承QRunnab ...

  2. Qt封装QTcpServer参考资料--QTcpServer多线程实现

    目的:每个客户端连接的tcpSocket分别分配一个专门的线程来处理. 实现时分别继承QTcpServer和QTcpScoket实现出自己需要的类. 继承QTcpServer为每个客户端连接时分配线程 ...

  3. Java基础 继承的方式创建多线程 / 线程模拟模拟火车站开启三个窗口售票

    继承的方式创建多线程 笔记: /**继承的方式创建多线程 * 线程的创建方法: * 1.创建一个继承于Thread 的子类 * 2.重写Thread类的run()方法 ,方法内实现此子线程 要完成的功 ...

  4. Qt 线程池QThreadPool类、QRunnable类

    QThreadPool类 用来管理 QThreads.此类中的所有函数都是线程安全的. 主要属性: 1.activeThreadCount: 此属性表示线程池中的活动线程数,通过activeThrea ...

  5. JAVA与多线程开发(线程基础、继承Thread类来定义自己的线程、实现Runnable接口来解决单继承局限性、控制多线程程并发)

    实现线程并发有两种方式:1)继承Thread类:2)实现Runnable接口. 线程基础 1)程序.进程.线程:并行.并发. 2)线程生命周期:创建状态(new一个线程对象).就绪状态(调用该对象的s ...

  6. QT 继承QWidget && 继承QDialog

    工作项目中,利用到Qt对话框,场景需求: 1. 一部分窗体需要继承自QWidget 2. 一部分窗体需要继承自QDialog 3. 两者均需要去掉标题栏图标,同时能够自由拖动. 如果两者分开继承实现, ...

  7. 继承Thread类使用多线程

    java实现多线程有两种方式,一种是继承Thread类,另外一种就是实现Runnable接口. 两种实现方法的优缺点: 使用Thread类实现多线程局限性就是不支持多继承,因为java是不支持类多继承 ...

  8. 多线程之继承Thread类及多线程内存分析

    *创建多线程的一种方式:继承Thread类 * java.lang.Thread是描述多线程的类,要实现多线程程序,一种方式就是继承Thread类 * 1.创建一个类Mythread让其extends ...

  9. 通过继承Thread类实现多线程

    (1)继承Thread类(2)重写run(方法(3)通过start0方法启动线程 一定的缺点: Java中的类是单继承的,一旦继承了Thread类,就不允许再去继承其它的类 线程和主方法之间的执行顺序 ...

随机推荐

  1. CentOS 7安装Nginx 1.10.2

    安装epel-release源并进行安装 yum install epel-release yum update(时间会有点长) yum install nginx 相关操作: systemctl s ...

  2. weblogic 安装+部署(一)

    昨天刚接触weblogic,在windows下搭建了一个weblogic,没什么技术,留个笔记. 1.首先要有jdk,添加环境变量这个没什么好说的. 2.下载weblogic:可以去官网下:http: ...

  3. C++vector and opencv Mat

    转载:https://blog.csdn.net/u012507022/article/details/50979011?utm_source=blogxgwz5 最近在写Opencv程序,用到离散小 ...

  4. C# Dropdownlist设置选择项

    (1)   dropdowslist.selectedIndex=索引值(数字); (2) dropdownlist.Items.findbyvalue(你的值).selected=true   (3 ...

  5. 用C写一个简单的推箱子游戏(二)

    下面接着上一篇随笔<用C写一个简单的推箱子游戏(一)>来写 tuidong()函数是用来判断游戏人物前方情况的函数,是推箱子游戏中非常重要的一个函数,下面从它开始继续介绍推箱子的小程序怎么 ...

  6. arcgis-java-100.8.0.jar下载

    链接: https://pan.baidu.com/s/1HoW2IhPvHRw9LBZphxC5Rw 提取码: pexn

  7. 【Curl】【转】curl用法!

    curl基础用法! www.ruanyifeng.com/blog/2019/09/curl-reference.html

  8. 晋城6397.7539(薇)xiaojie:晋城哪里有xiaomei

    晋城哪里有小姐服务大保健[微信:6397.7539倩儿小妹[晋城叫小姐服务√o服务微信:6397.7539倩儿小妹[晋城叫小姐服务][十微信:6397.7539倩儿小妹][晋城叫小姐包夜服务][十微信 ...

  9. day23 Pyhton学习 昨日回顾.re模块.序列化模块

    一.昨日回顾 #__file__查看当前文件所在的绝对路径 #time 时间模块 time.time 获取当前时间戳时间 字符串->time.strptime->结构化->mktim ...

  10. spring boot:使用多个线程池实现实现任务的线程池隔离(spring boot 2.3.2)

    一,为什么要使用多个线程池? 使用多个线程池,把相同的任务放到同一个线程池中,可以起到隔离的作用,避免有线程出错时影响到其他线程池,例如只有一个线程池时,有两种任务,下单,处理图片,如果线程池被处理图 ...