目录
前言
一、Linux下查看进程的情况
二、线程的创建
三、多线程的创建和使用
前言
以下引用内容源自正点原子Qt开发指南文档。
我们写的一个应用程序,应用程序跑起来后一般情况下只有一个线程,但是可能也有特殊情况。比如我们前面章节写的例程都跑起来后只有一个线程,就是程序的主线程。线程内的操作都是顺序执行的。恩,顺序执行?试着想一下,我们的程序顺序执行,假设我们的用户界面点击有某个操作是比较耗时的。您会发现界面点击完了,点击界面对应的操作还没有完成,所以就会冻结界面,不能响应,直到操作完成后,才返回到正常的界面里。如果我们的界面是这么设计的话,估计用户得发毛了。
这种情况我们一般是创建一个单独的线程来执行这个比较耗时的操作。比如我们使用摄像头拍照保存照片。恩,很多朋友问,这个不算耗时吧。对的在电脑上使用 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 的类。
一、Linux下查看进程的情况
-
进程 (Process):
- 进程是操作系统中资源分配的基本单位,代表一个正在执行的程序。每个进程都有自己的内存空间、系统资源(如文件描述符)和运行状态。
- 一个进程可以包含一个或多个线程。
-
线程 (Thread):
- 线程是进程中执行的最小单位。一个线程是一个轻量级的执行单元,多个线程共享同一进程的内存空间和资源。
- 线程之间的切换通常比进程切换更快,因为它们共享同一进程的上下文和资源,减少了上下文切换的开销。
总的来说,线程是CPU调度的最小单位,一个进程至少是有一个线程当然也可以有多个线程。这里打开你的命令行窗口( Ctrl+Alt+T),然后这里输入如下,查看当前系统的进程情况。
ps -aux
然后创建一个Qt项目,下面是这个按钮的槽函数,很显然当我们点击之后就会进到这个死循环里面去。
void Widget::on_pushButton_clicked()
{
qDebug()<<"click"<<endl;
while(1){
}
qDebug()<<"over"<<endl;
}
在你的Qtcreator去点击运行。
然后在命令行窗口输入:
ps -aux | grep QThread//QThread是Qt程序名字
这条指令是查找名字为QThread的进程
这里就看到我这个进程的PID为8455,然后我要去查看这个进程下有哪些子进程。
ps -m 8455
ps -m
是 Linux 中用于显示当前进程及其线程的命令。具体来说,它显示了与指定进程相关的线程的信息。这条命令可以帮助你查看进程的线程状态和相关信息。
这里就可以看到这个Qt程序带有另外的五个子线程,第一个是表示主线程,剩下几个是这个进程的分支也就是子线程。
然后回到你的QTcreator,点击这个按钮,然后就是卡死的情况了,这个程序就阻塞了。
好了现在就要处理这个东西了,这里就可以去用线程来去处理。
二、线程的创建
class MyThread : public QThread{
Q_OBJECT
public:
MyThread(QWidget* parent=nullptr){
Q_UNUSED(parent);
}
~MyThread(){
qDebug()<<"die"<<endl;
}
void run() override{
qDebug()<<"start "<<endl;
while(1){
}
deleteLater();
}
};
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) //
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//sadasdw
mythread= new MyThread(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
mythread->start();
}
这里在Widget调用构造函数的时候就创建了这个线程,当我们去点击按钮的时候就是线程的开始。这里就可以实现不出现卡死,这就是线程的调度。
三、多线程的创建和使用
Widget.h代码:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QThread>
#include <QtDebug>
class MyThread;
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::Widget *ui;
MyThread* mythread;
};
class MyThread : public QThread{
Q_OBJECT
public:
MyThread(QWidget* parent=nullptr){
Q_UNUSED(parent);
}
~MyThread(){
qDebug()<<"die"<<endl;
}
void run() override{
qDebug()<<"start "<<endl;
msleep(5000);
qDebug()<<"over "<<endl;
deleteLater();
}
};
#endif // WIDGET_H
这里我有一个MyThread线程的类,这个类重写了父类run()方法,当线程开启的时候这个就会调用这个方法。当run方法执行到最后一行的时候要用deleteLater()来去把这个线程的对象删除掉,否则堆区的内存就会无法释放。
下面是Widget.cpp文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) //
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//sadasdw
//mythread= new MyThread(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
// mythread->start();
MyThread *testThread = new MyThread(this);
testThread->start();
}
这里就是点击一个按钮就会开启一个线程,每次点击之后都会去开启线程,这就是多线程的创建。
今日壁纸: