Qt线程之QRunnable的使用详解

更新时间:2023-06-30 09:57:05 阅读: 评论:0

Qt线程之QRunnable的使⽤详解
概述
说到线程通常会想到QThread,但其实Qt中创建线程的⽅式有多种,这⾥主要介绍其中⼀种QRunnable,QRunnable和QThread⽤法有些不同,并且使⽤场景也有区别。接下来就来看看QRunnable的⽤法、使⽤场景以及注意事项。
⽤法
要使⽤QRunnable创建线程,步骤如下:
继承QRunnable。和QThread使⽤⼀样, ⾸先需要将你的线程类继承于QRunnable。
重写run函数。还是和QThread⼀样,需要重写run函数,run是⼀个纯虚函数,必须重写。
使⽤QThreadPool启动线程
和QThread的区别
与外界通信⽅式不同。由于QThread是继承于QObject的,但QRunnable不是,所以在QThread线程中,
可以直接将线程中执⾏的结果通过信号的⽅式发到主程序,⽽QRunnable线程不能⽤信号槽,只能通过别的⽅式,等下会介绍。
在线英汉互译软件启动线程⽅式不同。QThread线程可以直接调⽤start()函数启动,⽽QRunnable线程需要借助QThreadPool进⾏启动。
资源管理不同。QThread线程对象需要⼿动去管理删除和释放,⽽QRunnable则会在QThreadPool调⽤完成后⾃动释放。
⽰例
⾸先我们新建⼀个类,继承于QRunnable
#include <QRunnable>
#include <QWidget>
class CusRunnable : public QRunnable
{
教导主任竞聘演讲稿
public:
explicit CusRunnable();
~CusRunnable();
void run();
};
#endif // CUSRUNNABLE_H
源⽂件
ashworth#include "cusrunnable.h"
#include <QDebug>
#include <QThread>
CusRunnable::CusRunnable()
lamento
{
}
CusRunnable::~CusRunnable()
{
qDebug() << __FUNCTION__;
}
void CusRunnable::run()
{
qDebug() << __FUNCTION__ << QThread::currentThreadId();
QThread::msleep(1000);
}
然后在主界⾯中调⽤该线程。
#include <QWidget>
#include "cusrunnable.h"
class Widget : public QWidget
{
Q_OBJECT
public:
lie的过去式和过去分词Widget(QWidget *parent = 0);
~Widget();
private:
CusRunnable * m_pRunnable = nullptr;
};
#endif // WIDGET_H
#include "widget.h"
#include <QThreadPool>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_pRunnable = new CusRunnable(this);
qDebug() << __FUNCTION__  << QThread::currentThreadId();
QThreadPool::globalInstance()->start(m_pRunnable);
}
Widget::~Widget()
{
qDebug() << __FUNCTION__ ;
}
输出结果:
Widget::Widget 0x199c
CusRunnable::run 0x1bcc
CusRunnable::~CusRunnable
我们可以看到这⾥打印的线程ID是不同的,说明是在不同线程中执⾏,⽽线程执⾏完后就⾃动进⼊到析构函数中, 不需要⼿动释放。
26个字母怎么学最快启动线程⽅式
上⾯我们说到要启动QRunnable线程,需要QThreadPool配合使⽤,⽽调⽤⽅式有两种:全局线程池和⾮全局线程池。
全局线程池
上⾯⽰例中启动⽅式就是⽤的全局线程池来启动QRunnable,使⽤很简单:
QThreadPool::globalInstance()->start(m_pRunnable);
⾮全局线程池
除此之外,还可以使⽤⾮全局线程池的⽅式来实现,该⽅式可以控制线程最⼤数量, 以及其他设置,⽐较灵活,具体参照帮助⽂档。
QThreadPool  threadpool;
threadpool.tMaxThreadCount(1);
threadpool.start(m_pRunnable);
与外界通信
前⾯我们提到,因为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(), QGenericArg ument 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,失败返回fal,具体使⽤⽅法就不在这⾥介绍。
QMetaObject::invokeMethod可以是异步调⽤,也可以是同步调⽤。这取决与它的连接⽅式Qt::ConnectionType type。如果type为Qt::DirectConnection,则为同步调⽤,若为Qt::QueuedConnection,则为异步调⽤。
来看⼀下,在上⾯的⽰例中,我们如何通过QMetaObject::invokeMethod让QRunnable线程与外部主线程通信。
假如我们在主界⾯中定⼀个函数,⽤于更新界⾯内容:
Q_INVOKABLE void tText(QString msg);
然后线程类需要修改⼀下:
class CusRunnable : public QRunnable
{
public:
explicit CusRunnable(QObject *obj);
~CusRunnable();
void run();
private:
QObject * m_pObj = nullptr;
};
CusRunnable::CusRunnable(QObject * obj):
m_pObj(obj)
{
}
CusRunnable::~CusRunnable()
{
qDebug() << __FUNCTION__;
}
void CusRunnable::run()
{
qDebug() << __FUNCTION__ << QThread::currentThreadId();
QMetaObject::invokeMethod(m_pObj,"tText",Q_ARG(QString,"this is AA!"));
QThread::msleep(1000);
}
注意,这⾥的调⽤⽅式:
QMetaObject::invokeMethod(m_pObj,"tText",Q_ARG(QString,"this is AA!"));
其中"tText"就是要调⽤的函数,传参⽅式Q_ARG(QString,"this is AA!"),表⽰传⼊⼀个QString类型,值为"this is AA!"
⽽在创建线程对象时,需要将主界⾯对象传⼊线程类
m_pRunnable = new CusRunnable(this);
这样⼀来就可以在线程中和外界通信了。
遇到的问题viking
使⽤QThreadPool调⽤线程后,线程执⾏完会⾃动释放资源,但是我使⽤的时候遇到⼀个问题,未找到原因。就是在该线程对象都已经释放过后(已经进⼊到析构函数了),该对象还可以继续使⽤,还可以使⽤该对象调⽤线程类中的函数,也就是说,理论上该对象指针已经释放了,这时候变成了野指针,并且线程对象值不为空。
做了⼀个试验,在执⾏完线程后,再次使⽤线程对象指针调⽤线程类中的函数,结果还是可以⽤。
关键代码如下:
m_pRunnable = new CusRunnable(this);
qDebug()<< __FUNCTION__  << QThread::currentThreadId();
QThreadPool::globalInstance()->start(m_pRunnable);
m_pBtn = new QPushButton(this);
m_pBtn->tText("click me");
connect(m_pBtn,&QPushButton::clicked,this,[=](){
qDebug() << __FUNCTION__ << m_pRunnable;
m_pRunnable->Func();
小学三年级英语
});
这⾥每次点击按钮,就会调⽤⼀次线程类中的函数。输出如下:
Widget::Widget 0x3794
CusRunnable::run 0x39e0
Widget::tText "this is AA!"
CusRunnable::~CusRunnable
Widget::{ctor}::<lambda_e0d3aa46abd43a56277208964007553d>::operator () 0x1045348
Widget::{ctor}::<lambda_e0d3aa46abd43a56277208964007553d>::operator () 0x1045348
广州成人教育从上⾯可以看到,已经进⼊到了析构函数,但是依然可以使⽤该对象调⽤其中的函数,并且对象也不为空。
再次试验,在主界⾯析构函数中判断:
Widget::~Widget()
{
love actuallyqDebug()<< __FUNCTION__ ;
if(m_pRunnable){
delete m_pRunnable;
m_pRunnable = nullptr;
}
}
关闭程序的时候,会出现异常退出,也就是说,该指针确实释放了,不能再次delete,变成了⼀个野指针。
所以最终结论,QThreadPool在调⽤线程执⾏完后确实会释放资源,但是并不会将对象置空,这时候对象变成了⼀个野指针,如果还是⽤这个指针的话可能会出现隐患错误,所以最好不好这样调⽤。⽽在程序最终退出时,可以将该线程对象置空,但是不能再次delete,否则将会异常。

本文发布于:2023-06-30 09:57:05,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/162469.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:线程   对象   函数   需要   释放   启动
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图