Qt修炼手册11_多线程编程和QThread类

2023-12-02 11:58

本文主要是介绍Qt修炼手册11_多线程编程和QThread类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.事件循环

学习QT多线程编程之前,有必要先熟悉事件循环的概念。
先看一个单线程界面程序的主函数代码:
int main(int argc, char* argv[])
{QApplication app(argc,  argv);// 构造主窗口对象并显示MainWindow w;w.show(); // 进入事件循环return app.exec();
}
在程序初始化完成后,主线程进入main()函数开始执行应用代码。一般地,我们在主线程上构建界面对象然后进入事件循环以处理控件绘制、用户输入、系统输出等消息。这就是我们通常说的事件驱动模型。
这里需要提一个问题,为什么需要多线程编程?
多线程编程旨在提高人机交互的感受。主线程承担着用户交互的重任,当在主线程上运行费时的代码时,就会影响用户的正常操作。所以我们常把一些费时费力的计算工作移出主线程,开辟新的线程来运行之。
QThread是QT中用于线程管理的类,调用一个QThread对象的start()方法时,会创建一个新的线程并执行它的run()方法。默认地,run()会调用exec()方法进入自己的消息循环中。如下图所示:

上图中有主线程、工作线程都是执行事件循环,并且注意到主线程内部含有thr、w、objs这些QObject对象(这些对象都是在主线程上创建的)。主线程的事件循环负责检测这些对象是否有消息要处理,有的话则调用对象的slot方法。可以使用QObject::moveToThread方法将某个对象移到其他线程中,譬如:
class Worker : public QObject {Q_OBJECT…
}void someFunc()
{QThread thr = new QThread;Worker worker = new Worker;worker->moveToThread(thr);thr->start();…
}
如果在主线程上调用someFunc(),则thr和worker在创建后关联在主线程上,当调用worker-> moveToThread()后,worker对象关联到了新的线程中,如图所示:

2.QThread类

2.1 类基础

QThread类可以不受平台影响而实现线程。QThread提供在程序中可以控制和管理现成的多种成员函数和信号/槽。通过QThread类的成员函数start()启动线程。
class Worker : public QObject
{Q_OBJECT
public slots:void doWork(){......}
};void MyObject::putWorkerInThread()
{Worker *worker = new Worker;QThread *workerThread = new QThread(this);connect(workerThread, &QThread::started, worker, &Worker::doWork);connect(workerThread, &QThread::finished,worker, &Worker::deleteLater);worker->moveToThread(workerThread);//开始进行事件循环,并发射信号workerThread->start();workerThread->start();
}
上述代码中,Worker类的槽启动分离线程时发送的信号线程终止时发送的信号相关联。如果我们启动了线程,就会调用Worker类的槽函数doWork()。

换言之,如果运行putWorkerInThread()的函数start(),那么就相当于发送了线程的启动信号(QThread::started),根据定义的信号-槽,程序就会调用与函数connect()相关联的Worker类的槽函数doWorker()。如果结束线程,则自动发送QThread::finished信号,发送此信号后,立即释放Worker使用过的线程内存区域。

QThread通过信号函数started()和finished()通知开始和结束,并查看线程状态。可以确认究竟是使用函数isfinished()信号终止线程,还是使用函数isRunning()启动线程。使用函数exit()和quit()可以结束线程。

2.2 多线程初步

如果使用多线程,有时需要等到所有线程终止。此时,使用函数wait()即可。线程中,使用成员函数sleep()、msleep()、usleep()可以暂停秒、毫秒及微秒单位的线程。

3.QThread使用的内存区域

QThread使用的内存区域分为线程私有区域和共享内存区域。线程内部使用的寄存器区域只能在线程内部共享。共享数据区域可以访问其他线程。
线程内部使用的寄存器和栈区域如下图所示:

线程内部共享区域虽然不能访问其他线程,但栈区域可以在线程间共享。因此,如果实现多种线程访问栈区域,需要注意互斥体,读写锁等线程的安全性。

3.多线程编程实例与解析

widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QtWidgets/QWidget>
#include "ui_widget.h"
#include <QThread>
#include <QPushButton>
#include <QMutex>
class MyThread : public QThread
{Q_OBJECT
public:MyThread(int num);
private:bool threadStop;int number;QMutex mutex;
public:void stop();
protected:void run();
};
/
class widget : public QWidget
{Q_OBJECT
public:widget(QWidget *parent = 0);~widget();MyThread *thread1;MyThread *thread2;
private slots:void btn_start();void btn_stop();void btn_isRunning();void btn_isFinished();
private:Ui::widgetClass ui;
};#endif // WIDGET_H
widegt.cpp
#include "widget.h"
MyThread::MyThread(int num)
{	number = num;	}void MyThread::stop()
{threadStop = true;qDebug("[%d] Thread stop", number);
}void MyThread::run()
{threadStop = false;int i = 0;while(!threadStop){mutex.lock();qDebug("[%d] MyThread %d", number, i);i++;sleep(1);mutex.unlock();}
}///
widget::widget(QWidget *parent): QWidget(parent)
{ui.setupUi(this);thread1 = new MyThread(1);thread2 = new MyThread(2);QPushButton *btn_start = new QPushButton("START", this);btn_start->setGeometry(10, 10, 80, 40);QPushButton *btn_stop = new QPushButton("STOP", this);btn_stop->setGeometry(100, 10, 80, 40);QPushButton *btn_isRunning = new QPushButton("IsRunning", this);btn_isRunning->setGeometry(200, 10, 100, 40);QPushButton *btn_isFinished = new QPushButton("IsFinished", this);btn_isFinished->setGeometry(310, 10, 100, 40);connect(btn_start,      SIGNAL(clicked()), this, SLOT(btn_start()));connect(btn_stop,       SIGNAL(clicked()), this, SLOT(btn_stop()));connect(btn_isRunning,  SIGNAL(clicked()), this, SLOT(btn_isRunning()));connect(btn_isFinished, SIGNAL(clicked()), this, SLOT(btn_isFinished()));
}void widget::btn_start()
{thread1->start();thread2->start();
}void widget::btn_stop()
{thread1->stop();thread2->stop();
}void widget::btn_isRunning()
{if(thread1->isRunning())qDebug("[1] Thread is running");elseqDebug("[1] Thread is not running");if(thread2->isRunning())qDebug("[2] Thread is running");elseqDebug("[2] Thread is not running");
}void widget::btn_isFinished()
{if(thread1->isFinished())qDebug("[1] Thread Finish");elseqDebug("[1] Thread not Finish");if(thread2->isFinished())qDebug("[2] Thread Finish");elseqDebug("[2] Thread not Finish");
}widget::~widget()
{}
输出结果:
代码分析:
1.QThread信号与槽
  • 信号:终止线程实例运行发送信号(void finished)、启动线程实例发送信号(void started)、结束线程实例发送信号(void terminated)
  • 槽:线程中止运行槽(finished)、线程启动槽(start)、线程结束槽(terminate)
2.QThread优先级
QThread通过函数setPriority()设置优先级。

4.关于QDebug的一点思考                                               

VS2010开发Qt,怎么显示qDebug信息(添加DOS窗口):
  

这篇关于Qt修炼手册11_多线程编程和QThread类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/445252

相关文章

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

16.Spring前世今生与Spring编程思想

1.1.课程目标 1、通过对本章内容的学习,可以掌握Spring的基本架构及各子模块之间的依赖关系。 2、 了解Spring的发展历史,启发思维。 3、 对 Spring形成一个整体的认识,为之后的深入学习做铺垫。 4、 通过对本章内容的学习,可以了解Spring版本升级的规律,从而应用到自己的系统升级版本命名。 5、Spring编程思想总结。 1.2.内容定位 Spring使用经验

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

QT 中ListView和ListWidget有什么区别

ListView和ListWidget在Qt框架中都是用于显示列表数据的控件,但它们在使用方法和特性上存在一些明显的差异。以下是关于它们用法不一样的地方的详细分析: 数据管理方式: ListView:使用QAbstractItemModel数据模型来管理和显示列表数据。QAbstractItemModel是一个抽象类,允许开发者自定义数据模型以适应特定的数据结构和需求。这使得ListView在处

IPython小白教程:提升你的Python交互式编程技巧,通俗易懂!

IPython是一个增强的Python交互式shell,它提供了丰富的功能和便捷的交互方式,使得Python开发和数据分析工作更加高效。本文将详细介绍IPython的基本概念、使用方法、主要作用以及注意事项。 一、IPython简介 1. IPython的起源 IPython由Fernando Pérez于2001年创建,旨在提供一个更高效的Python交互式编程环境。 2. IPyt

从《深入设计模式》一书中学到的编程智慧

软件设计原则   优秀设计的特征   在开始学习实际的模式前,让我们来看看软件架构的设计过程,了解一下需要达成目标与需要尽量避免的陷阱。 代码复用 无论是开发何种软件产品,成本和时间都最重要的两个维度。较短的开发时间意味着可比竞争对手更早进入市场; 较低的开发成本意味着能够留出更多营销资金,因此能更广泛地覆盖潜在客户。 代码复用是减少开发成本时最常用的方式之一。其意图

如何使用Qt的PIMPL习惯用法(PIMPL Idiom

) PIMPL是指“Pointer to IMPLementation”(指向实现的指针),意味着将实现细节隐藏起来,用户类无需关注这些实现细节。在Qt中常用PIMPL习惯用法来清晰地区分接口与实现,尽管Qt官方文档并未详细说明该机制。本文将演示如何在Qt中使用PIMPL习惯用法,并以一个简单的坐标输入对话框作为实例。 原理与动机 PIMPL的核心在于将类的实现细节封装在一个私有类中,这个私

【Qt6.3 基础教程 17】 Qt布局管理详解:创建直观和响应式UI界面

文章目录 前言布局管理的基础为什么需要布局管理器? 盒布局:水平和垂直排列小部件示例:创建水平盒布局 栅格布局:在网格中对齐小部件示例:创建栅格布局 表单布局:为表单创建标签和字段示例:创建表单布局 调整空间和伸缩性示例:增加弹性空间 总结 前言 当您开始使用Qt设计用户界面(UI)时,理解布局管理是至关重要的。布局管理不仅关系到UI的外观,更直接影响用户交互的体验。本篇博

【Qt6.3 基础教程 16】 掌握Qt中的时间和日期:QTimer和QDateTime的高效应用

文章目录 前言QTimer:定时任务的强大工具QTimer的基本用法高级特性:单次定时器 QDateTime:处理日期和时间获取当前日期和时间日期和时间的格式化输出日期和时间计算 用例:创建一个倒计时应用结论 前言 在开发桌面应用程序时,处理时间和日期是一个常见且重要的任务。Qt框架提供了强大的工具来处理与时间相关的功能,其中QTimer和QDateTime是最核心的类。本