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类相关推荐

  1. Qt的Tcp服务器多线程编程-附带代码展示

    Qt的Tcp服务器多线程编程-附带代码展示 该程序主要实现tcp服务器如何使用多线程的方式来连接多个客户端,此文章没有实现客户端的多线程编程. 创建子线程时需要注意的点: 1.子线程与主线程之间交互数 ...

  2. 详解Java多线程编程中LockSupport类的线程阻塞用法

    转载自  详解Java多线程编程中LockSupport类的线程阻塞用法 LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语.LockSupport实际 ...

  3. Qt修炼手册12_线程同步与线程等待条件

    1.同步线程:何为同步? 参考百度百科: 1.1 线程 线程是进程中的一个实体,是被系统独立调度和分配的基本单位.一个进程可以有多个线程,一个线程必须有一个父进程,线程自己不拥有系统资源,只有运行必须 ...

  4. C 多线程编程之在类中使用多线程(thread)的方法

    一.thread的基本用法 参见C++使用thread类多线程编程 . 二.类外使用多线程,访问类的成员 这几种方式,新建线程都是在类外,然后通过把友元函数或者成员函数作为thread参数. #inc ...

  5. Qt修炼手册6_图形:图形视图框架

    1.前言 主要为了学习可以在GUI上有效显示或管理大量图形对象的API,以及进行显示.扩大.缩小等操作. 2.图形视图框架(Graphics View Framework) 图形视图框架使用BSP树算 ...

  6. Qt修炼手册10_QTableWidget控件使用说明及实践

    1.QTableWidget类简介 QTableWidget是QT对话框设计中常用的显示数据表格的控件. QTableWidget继承于类QTableView,所以在学习QTableWidget之前可 ...

  7. Qt修炼手册9_Ui名字空间及setupUi()原理解读

    1.前言 用最新的QtCreator选择GUI的应用会产生含有如下文件的工程: 1.1 *.pro文件 QT += core gui //使用Qt的Core和Gui模块.QT将自己的库函数分为多个模块 ...

  8. Qt修炼手册8_常用的容器类QVector和QList

    1.前言 Qt提供了非常有用的容器类的迭代器类,用于修改.删除.插入和保存数据.本着学习的角度,重点研究了QVector和QList两个类. QT中,具有STL风格的迭代器具有两种数据类型,分别是只读 ...

  9. Qt修炼手册7_图形:用户自定义QGraphicsItem

    1.前言 Qt中提供的Item未必能够满足需要,因此有必要实现自定义的QGraphicsItem对象.与QPushButton一样,如果发生鼠标事件,那么为了更换被点击按钮的图像,可以使用paint实 ...

最新文章

  1. mysql使用sha256密码,MySQL5.6启用sha256_password插件
  2. ren`guang-boss面试
  3. 用户 'sa' 登录失败。原因: 未与信任 SQL Server 连接相关联。
  4. Spring Cloud面试题(2020最新版)
  5. android studio云测,Android studio 下的robotium自动化测试和持续集成
  6. move std 函数 示例_C++ STL迭代器辅助函数
  7. eja智能压力变送器工作原理_压力变送器的原理、接线和安装方法
  8. 高斯拟合原理_数据分析中的插值与拟合(2) —— 拟合
  9. android lottie字体json,Android Lottie集成及基本用法
  10. java 自定义进度条_JAVA Swing 自定义进度条样式(简单实现)
  11. GitLab使用教程(详细)
  12. 2016春招腾讯笔试题
  13. 色温CCT与色坐标xy互换
  14. 各种蔬菜水果中英文名称对照
  15. 纵向数据中抑郁检测与预测的深度多任务学习
  16. 虚数的现实、物理意义是什么?
  17. windows下x265编译
  18. 自动化立体仓库系统实训
  19. 【笔记】WGAN GP :WGAN自己的李普西斯条件是gradient clipping(大部分weight是正负0.01),在此基础上增加新的motivation让WGAN GP实现李普西斯条件
  20. 程序员:必备技能 Git

热门文章

  1. Android 程序启动界面Demo
  2. 阔步向前冲,拥抱云计算-【软件和信息服务】2012.10
  3. JPA的多表复杂查询
  4. 新建django项目
  5. msfvenom 摄像头
  6. yii框架安装及环境配置!!!
  7. gcc/g++/makefile/easymake/cmake/xmake/nmake ...
  8. 安装logstash,elasticsearch,kibana三件套
  9. 154在屏幕中绘图时设置透明度(扩展知识:为图片视图添加点击手势识别器,来实现点击事件操作)...
  10. HtmlAgilityPack 之 HtmlNode类