简述

前面,我们介绍了QThread常用的两种方式:

worker-object

子类化QThread

下面,我们首先来看看子类化QThread在日常中的应用。

一般情况下,QThread进行耗时操作的同时会与UI进行交互,比如:显示进度、旋转等待。。。进行友好型的交互,让用户知道当前的操作。

子类化QThread

我们以更新进度条为例,来模拟一个耗时操作,并分享我们有可能在此过程中遇到的问题及解决办法。

首先,我们定义一个WorkerThread 类,让其继承自QThread,并重写run()函数,每隔50毫秒更新下当前的值,然后发射resultReady()信号(用于更新进度条)。

#include

class WorkerThread : public QThread

{

Q_OBJECT

public:

explicit WorkerThread(QObject *parent = 0)

: QThread(parent)

{

qDebug() << "Worker Thread : " << QThread::currentThreadId();

}

protected:

virtual void run() Q_DECL_OVERRIDE {

qDebug() << "Worker Run Thread : " << QThread::currentThreadId();

int nValue = 0;

while (nValue < 100)

{

// 休眠50毫秒

msleep(50);

++nValue;

// 准备更新

emit resultReady(nValue);

}

}

signals:

void resultReady(int value);

};

构建一个主界面,用于显示进度条。

class MainWindow : public CustomWindow

{

Q_OBJECT

public:

explicit MainWindow(QWidget *parent = 0)

: CustomWindow(parent)

{

qDebug() << "Main Thread : " << QThread::currentThreadId();

// 创建开始按钮、进度条

QPushButton *pStartButton = new QPushButton(this);

m_pProgressBar = new QProgressBar(this);

//设置文本、进度条取值范围

pStartButton->setText(QString::fromLocal8Bit("开始"));

m_pProgressBar->setFixedHeight(25);

m_pProgressBar->setRange(0, 100);

m_pProgressBar->setValue(0);

QVBoxLayout *pLayout = new QVBoxLayout();

pLayout->addWidget(pStartButton, 0, Qt::AlignHCenter);

pLayout->addWidget(m_pProgressBar);

pLayout->setSpacing(50);

pLayout->setContentsMargins(10, 10, 10, 10);

setLayout(pLayout);

// 连接信号槽

connect(pStartButton, SIGNAL(clicked(bool)), this, SLOT(startThread()));

}

~MainWindow(){}

private slots:

// 更新进度

void handleResults(int value)

{

qDebug() << "Handle Thread : " << QThread::currentThreadId();

m_pProgressBar->setValue(value);

}

// 开启线程

void startThread()

{

WorkerThread *workerThread = new WorkerThread(this);

connect(workerThread, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));

// 线程结束后,自动销毁

connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));

workerThread->start();

}

private:

QProgressBar *m_pProgressBar;

WorkerThread m_workerThread;

};

显然,主线程、Worker构造所在的线程、槽函数处于同一线程,而run()函数处于其它线程。

Main Thread : 0x34fc

Worker Thread : 0x34fc

Worker Run Thread : 0x4038

Handle Thread : 0x34fc

在主线程中更新UI

当连接方式改为“Qt::DirectConnection”时:

connect(workerThread, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)), Qt::DirectConnection);

会出现以下错误:

ASSERT failure in QCoreApplication::sendEvent: “Cannot send events to objects owned by a different thread. Current thread e346e8. Receiver customWidget’ (of type ‘MainWindow’) was created in thread 4186a0”, file kernel\qcoreapplication.cpp, line 553

这时,线程的运行情况如下所示:

Main Thread : 0x2c6c

Worker Thread : 0x2c6c

Worker Run Thread : 0x4704

Handle Thread : 0x4704

也就是说,run()函数与槽函数所在的是同一个线程。

根据Qt之Threads和QObjects中“跨线程的信号和槽”部分,提到的“Direct Connection:当信号发射后,槽函数立即被调用。槽函数在信号发射者所在的线程中执行,而未必需要在接收者的线程中。”

所以,不难理解,因为我们在子线程中更新了UI。

正常结束线程

上述代码有一些问题,都是我们经常遇到的。

多次connect信号与槽:当多次点击“开始”按钮的时候,就会启动多个线程,同时更新进度条。

暴力终止线程:而且当用户关闭窗体的时候,线程有可能还在运行,这时极有可能发生未知错误。

正如前面提到过terminate(),比较危险,不鼓励使用。线程可以在代码执行的任何点被终止。线程可能在更新数据时被终止,从而没有机会来清理自己,解锁等等。。。总之,只有在绝对必要时使用此函数。

举一个简单的例子:当你的哥们正在酣睡时,你突然泼了一盆凉水,想象一下后果?O(∩_∩)O哈哈~

为了避免以上问题,我们先修改如下:

class MainWindow : public CustomWindow

{

Q_OBJECT

public:

explicit MainWindow(QWidget *parent = 0)

: CustomWindow(parent)

{

// ...

connect(&m_workerThread, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));

}

~MainWindow(){}

private slots:

// ...

void startThread()

{

if (!m_workerThread.isRunning())

m_workerThread.start();

}

private:

WorkerThread m_workerThread;

};

这时,不会出现启动多个线程的问题。但是如果线程正在运行,我们就关闭主界面,则会出现另外一个很常见的问题:

QThread: Destroyed while thread is still running

这是因为次线程还在运行,就结束了UI主线程。这个问题在耗时操作中经常遇到,我们可以采用以下方式来避免:

QMutex互斥锁 + bool成员变量。

#include

#include

class WorkerThread : public QThread

{

Q_OBJECT

public:

explicit WorkerThread(QObject *parent = 0)

: QThread(parent),

m_bStopped(false)

{

qDebug() << "Worker Thread : " << QThread::currentThreadId();

}

~WorkerThread()

{

stop();

quit();

wait();

}

void stop()

{

QMutexLocker locker(&m_mutex);

m_bStopped = true;

}

protected:

virtual void run() Q_DECL_OVERRIDE {

qDebug() << "Worker Run Thread : " << QThread::currentThreadId();

int nValue = 0;

while (nValue < 100)

{

// 休眠50毫秒

msleep(50);

++nValue;

// 准备更新

emit resultReady(nValue);

// 检测是否停止

{

QMutexLocker locker(&m_mutex);

if (m_bStopped)

break;

}

// locker超出范围并释放互斥锁

}

}

signals:

void resultReady(int value);

private:

bool m_bStopped;

QMutex m_mutex;

};

requestInterruption() + isInterruptionRequested()

这两个接口是Qt5以后引入的,使用很方便:

class WorkerThread : public QThread

{

Q_OBJECT

public:

explicit WorkerThread(QObject *parent = 0)

: QThread(parent)

{

}

~WorkerThread() {

// 请求终止

requestInterruption();

quit();

wait();

}

protected:

virtual void run() Q_DECL_OVERRIDE {

// 是否请求终止

while (!isInterruptionRequested())

{

// 耗时操作

}

}

};

在耗时操作中使用isInterruptionRequested()来判断是否请求终止线程,如果没有,则一直运行;当希望终止线程的时候,调用requestInterruption()即可。

更多参考

qt qthead里如何响应信号_Qt之QThread(深入理解)相关推荐

  1. qt qthead里如何响应信号_Qt 中的多线程技术(翻译)

    原文链接 Multithreading Technologies in Qt​doc.qt.io 正文 Qt 提供一系列的类与函数来处理多线程.Qt 开发者们可以使用下面四种方法来实现多线程应用. Q ...

  2. qt qthead里如何响应信号_QT使用教程(五)之程序编写

    第一个Qt小程序 1 最简单的Qt小程序 #include "widget.h" #include <QApplication>int main(int argc, c ...

  3. qt线程如何接收linux信号,Linux-Qt使用QThread多线程isRunning标志量问题

    ---恢复内容开始--- 摘要 Qt帮助文档中是这样介绍的: bool QThread::isRunning () constReturnstrue if the thread is running; ...

  4. Qt源码分析之信号和槽机制

    Qt的信号和槽机制是Qt的一大特点,实际上这是和MFC中的消息映射机制相似的东西,要完成的事情也差不多,就是发送一个消息然后让其它窗口响应,当然,这里的消息是广义的 说法,简单点说就是如何在一个类的一 ...

  5. Qt核心特性之 —— 「信号(Signal)与槽(Slot)」机制

    目录 1.Qt 与 Qt Creator简介: 2.关于引用头文件的一些事儿: 3.信号(Signal)与槽(Slot)机制: 3.1.一个小例子: 4.自定义信号与槽: 4.1.运行效果: 5.信号 ...

  6. Qt计算器开发(二):信号槽实现数学表达式合法性检查

    表达式的合法性 由于我们的计算器不是单步计算的,所以我们能够一次性输入一个长表达式.然而假设用户输入的长表达式不合法的话,那么就会引发灾难.所以有必要对于用户的输入做一个限制. 一些限制举例: 比方, ...

  7. qt 子窗口与父窗口数据通信_Qt实例--主窗口和子窗口互发信号

    准备工作: 需要首先添加一个Qt设计师界面类,这里使用默认类名Form. 实例一:主窗口向子窗口发送信号 在主窗口添加一个按钮QPushButton,在子窗口添加一个标签QLabel. 主窗口添加一个 ...

  8. qwidget show 是否有信号_QT中的消息传递与函数回调机制:信号(signal)和槽(slot)...

    Qt是一个跨平台的C++应用程序开发框架,被广泛用于开发GUI程序. 信号和槽是Qt的核心,就相当于MFC的消息传递和回调函数一样.只不过功能比MFC 消息处理的机制和回调函数更强大.GUI应用程序要 ...

  9. Qt从入门到放弃——信号和槽机制(SigalSlot)

    标题信号和槽概念 Qt提供了信号和槽机制用于完成界面操作的响应,信号和槽机制是完成任意两个Qt对象之 间的通信机制.其中,信号会在某个特定情况或动作下被触发,槽是等同于接收并处理信号的函数. 例如,窗 ...

最新文章

  1. BCH钱包的“现金”支持比特币现金NFC交易
  2. 2018-2019-1 20165201 《信息安全系统设计基础》第9周学习总结
  3. lottie动画_神器基于Lottie的动效设计平台 ─ 犸良,零基础轻松做出动画效果
  4. LeetCode 6055. 转化时间需要的最少操作数(贪心)
  5. SQL SERVER与ACCESS、EXCEL的数据转换
  6. (转)Android属性设置android:noHistory=true
  7. C++ 20发布后,这个老牌编程语言又“真香”了
  8. Windows 10, version 22H2 (released Oct 2022) 简体中文版、英文版下载
  9. 中国Android应用商店汇总
  10. chrome打开html文件显示不全,谷歌浏览器显示不全怎么办_chrome浏览器打开的网页显示不完整如何解决-win7之家...
  11. 关于Winxp U盘无法复制磁盘写保护解决办法
  12. win10无法装载iso文件_Windows 10更新后无法装载iso虚拟光盘
  13. 带你揭秘学习编程的7大优势!还有可以提升文化课的成绩?
  14. VueCompilerError: v-html will override element children.
  15. MikroTik RouterOS官网硬件一键开启DDNS动态域名解析,解决远程访问及映射/DDNS脚本
  16. 在EXCEL中使用VLOOKUP函数 快速批量查找对应内容
  17. 阿里云服务器华北1、华北2、华北3、华北5地域在哪个城市为什么没有华北4?... 1
  18. 计算机软考中级职称能积分?
  19. Java编程思想第四版第六章习题
  20. 终极反馈装置UFD-1.系统架构与设计制作步骤

热门文章

  1. bzoj3190 [JLOI2013]赛车 半平面交
  2. 2017.9.24 虔诚的墓主人 思考记录
  3. 打开c盘_为什么你的C盘总是爆满?教你彻底清理C盘空间,瞬间提速50%
  4. linux系统安装并配置oracle客户端
  5. java事务代码_关于java中实现JDBC事务控制代码示例
  6. 性能测试、 障碍条件和回滚
  7. Coding the Matrix Week 2 The Vector Space作业
  8. torch的DataLoader 浅析
  9. C++(Leetcode):两数之和
  10. 开源wms php,BIWEB WMS门户网站PHP开源建站系统