1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html 3)对正点原子Linux感兴趣的同学可以加群讨论:935446741
4)关注正点原子公众号,获取最新资料更新
我们写的一个应用程序,应用程序跑起来后一般情况下只有一个线程,但是可能也有特殊情况。比如我们前面章节写的例程都跑起来后只有一个线程,就是程序的主线程。线程内的操作都是顺序执行的。恩,顺序执行?试着想一下,我们的程序顺序执行,假设我们的用户界面点击有某个操作是比较耗时的。您会发现界面点击完了,点击界面对应的操作还没有完成,所以就会冻结界面,不能响应,直到操作完成后,才返回到正常的界面里。如果我们的界面是这么设计的话,估计用户得发毛了。 这种情况我们一般是创建一个单独的线程来执行这个比较耗时的操作。比如我们使用摄像头拍照保存照片。恩,很多朋友问,这个不算耗时吧。对的在电脑上使用Qt拍照,处理起来非常快。根本也不需要开启一个线程来做这种事。但是我们是否考虑在嵌入式的CPU上做这种事情呢?嵌入式的CPU大多数都没有电脑里的CPU主频(几GHz)那么高,处理速度也不快。此时我们就需要考虑开多一个线程来拍照了。拍完照再与主线程(主线程即程序原来的线程)处理好照片的数据,就完成了一个多线程的应用程序了。 官方文档里说,QThread类提供了一种独立于平台的方法来管理线程。QThread对象在程序中管理一个控制线程。QThreads在run()中开始执行。默认情况下,run()通过调用exec()来启动事件循环,并在线程中运行Qt事件循环。您可以通过使用QObject::moveToThread()将worker对象移动到线程来使用它们。 QThread线程类是实现多线程的核心类。Qt有两种多线程的方法,其中一种是继承QThread的run()函数,另外一种是把一个继承于QObject的类转移到一个Thread里。Qt4.8之前都是使用继承QThread的run()这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。两种方法区别不大,用起来都比较方便,但继承QObject的方法更加灵活。所以Qt的帮助文档里给的参考是先给继承QObject的类,然后再给继承QThread的类。 另外Qt提供了QMutex、QMutexLocker、QReadLocker 和QWriteLocker等类用于线程之间的同步,详细可以看Qt的帮助文档。 本章介绍主要如何使用QThread实现多线程编程,讲解如何通过继承QThread和QObject的方法来创建线程。还会使用QMutexLocker正确的退出一个线程。本章的内容就是这么多,并不深入,所以不难,目的就是快速掌握Qt线程的创建,理解线程。
10.1 继承QThread的线程 在第十章的章节开头说过了,继承QThread是创建线程的一个普通方法。其中创建的线程只有run()方法在线程里的。其他类内定义的方法都在主线程内。恩,这样不理解?我们画个图捋一捋。
通过上面的图我们可以看到,主线程内有很多方法在主线程内,但是子线程,只有run()方法是在子线程里的。run()方法是继承于QThread类的方法,用户需要重写这个方法,一般是把耗时的操作写在这个run()方法里面。 10.1.1 应用实例 本例目的:快速了解继承QThread类线程的使用。 例05_qthread_example1,继承QThread类的线程(难度:一般)。项目路径为Qt/2/05_qthread_example1。本例通过QThread类继承线程,然后在MainWindow类里使用。通过点击一个按钮开启线程。当线程执行完成时,会发送resultReady(const QString &s)信号给主线程。流程就这么简单。 在头文件“mainwindow.h”具体代码如下。 mainwindow.h编程后的代码
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 05_qthread_example1
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-06
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include
5 #include
6 #include
7 #include
8
9 /* 使用下面声明的WorkerThread线程类 */
10 class WorkerThread;
11
12 class MainWindow : public QMainWindow
13 {
14 Q_OBJECT
15
16 public:
17 MainWindow(QWidget *parent = nullptr);
18 ~MainWindow();
19
20 private:
21 /* 在MainWindow类里声明对象 */
22 WorkerThread *workerThread;
23
24 /* 声明一个按钮,使用此按钮点击后开启线程 */
25 QPushButton *pushButton;
26
27 private slots:
28 /* 槽函数,用于接收线程发送的信号 */
29 void handleResults(const QString &result);
30
31 /* 点击按钮开启线程 */
32 void pushButtonClicked();
33 };
34
35 /* 新建一个WorkerThread类继承于QThread */
36 class WorkerThread : public QThread
37 {
38 /* 用到信号槽即需要此宏定义 */
39 Q_OBJECT
40
41 public:
42 WorkerThread(QWidget *parent = nullptr) {
43 Q_UNUSED(parent);
44 }
45
46 /* 重写run方法,继承QThread的类,只有run方法是在新的线程里 */
47 void run() override {
48 QString result = "线程开启成功";
49
50 /* 这里写上比较耗时的操作 */
51 // ...
52 // 延时2s,把延时2s当作耗时操作
53 sleep(2);
54
55 /* 发送结果准备好的信号 */
56 emit resultReady(result);
57 }
58
59 signals:
60 /* 声明一个信号,译结果准确好的信号 */
61 void resultReady(const QString &s);
62 };
63
64 #endif // MAINWINDOW_H
65
第36行,声明一个WorkerThread的类继承QThread类,这里是参考Qt的QThread类的帮助文档的写法。 第47行,重写run()方法,这里很重要。把耗时操作写于此,本例相当于一个继承QThread类线程模板了。 在源文件“mainwindow.cpp”具体代码如下。 mainwindow.cpp编程后的代码
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 05_qthread_example1
* @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-06
*******************************************************************/
1 #include "mainwindow.h"
2
3 MainWindow::MainWindow(QWidget *parent)
4 : QMainWindow(parent)
5 {
6 /* 设置位置与大小 */
7 this->setGeometry(0, 0, 800, 480);
8
9 /* 对象实例化 */
10 pushButton = new QPushButton(this);
11 workerThread = new WorkerThread(this);
12
13 /* 按钮设置大小与文本 */
14 pushButton->resize(100, 40);
15 pushButton->setText("开启线程");
16
17 /* 信号槽连接 */
18 connect(workerThread, SIGNAL(resultReady(QString)),
19 this, SLOT(handleResults(QString)));
20 connect(pushButton, SIGNAL(clicked()),
21 this, SLOT(pushButtonClicked()));
22 }
23
24 MainWindow::~MainWindow()
25 {
26 /* 进程退出,注意本例run()方法没写循环,此方法需要有循环才生效 */
27 workerThread->quit();
28
29 /* 阻塞等待2000ms检查一次进程是否已经退出 */
30 if (workerThread->wait(2000)) {
31 qDebug()
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?