3

QT学习第六天_五个板栗的技术博客_51CTO博客

 2 years ago
source link: https://blog.51cto.com/u_15515702/5612798
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

QT学习第六天

精选 原创

五个板栗 2022-08-23 16:38:01 博主文章分类:Qt ©著作权

文章标签 多线程 线程管理 qt 文章分类 C/C++ 编程语言 yyds干货盘点 阅读数292

一、为什么使用线程

通常情况下,应用程序都是在一个线程中执行操作。但是,当调用一个耗时操作(例如,大批量I/O或大量矩阵变换等CPU密集操作)时,用户界面常常会冻结。而使用多线程可以解决这一问题。

  • 提高应用程序响应速度。

       这对于图形界面开发的程序尤为重要,当一个操作耗时很长时,整个系统都会等待这个操作,程序就不能响应键盘、鼠标、菜单等操作,而使用多线程技术可将耗时长的操作置于一个新的线程,避免以上问题。

  • 使多CPU系统更加有效。

当前线程数不大于CPU数目时,操作系统可以调度不同的线程运行于不同的CPU上。

  • 改善程序结构。

一个既长又复杂的进程可以考虑分为多个线程,成为独立或半独立的运行部分,这样有利于代码的理解和维护。

二、线程的特点

多线程程序有以下几个特点:

  1. 多线程程序的行为无法预期,当多次执行程序时,每一次的结果都可能不同。
  2. 多线程的执行顺序无法保证,它与操作系统的调度策略和线程优先级等因素有关。
  3. 多线程的切换可能发生在任何时刻、任何地点。
  4. 多线程对代码的敏感度高,对代码的细微修改都可能产生意想不到的结果。

三、线程管理

在Qt中使用QThread 来管理线程。

方法一:(QT4)

QT学习第六天_qt

1.自定义一个类,继承于QThread,并且只有一个线程处理函数(和主线程不再同一个线程),这个线程处理函数就是重写父类中的run函数。

2.线程处理函数里面写入需要执行的复杂数据处理

3.启动线程不能直接调用run函数,需要使用对象来调用start函数实现线程启动

4.线程处理函数执行结束后可以定义一个信号来告诉主线程

5.最后关闭线程

/*******************mythread.h**************************/
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
/*自定义一个类重写线程处理函数*/
class Mythread : public QThread
{
Q_OBJECT
public:
explicit Mythread(QObject *parent = nullptr);
/*线程处理函数,不能直接调用,通过start简介调用*/
protected:
/*线程处理函数*/
void run();

signals:
/*自定义一个线程处理函数指向完成后的一个信号*/
void runDone();

};

#endif // MYTHREAD_H
/*******************mythread.cpp**************************/
#include "mythread.h"

Mythread::Mythread(QObject *parent) : QThread(parent)
{

}

void Mythread::run()
{
/*很复杂的数据处理*/
sleep(5);
/*执行结束后发送信号*/
emit runDone();
}
/*******************widget.cpp****************************/
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
/*定时器的创建*/
MyTimer = new QTimer(this);
/*定义自定义线程类对象*/
thread = new Mythread(this);

/*定时器启动自动触发timeout信号*/
connect(MyTimer, &QTimer::timeout, this, &Widget::dealtimer);
/*connect自定义线程类中的信号 可以知道线程处理函数执行完成*/
connect(thread, &Mythread::runDone, this, &Widget::dealthread);
/*关闭窗口时触发以下信号 关闭窗口后关闭线程*/
connect(this, &Widget::destroyed, this, &Widget::stopthread);
}

Widget::~Widget()
{
delete ui;
}

void Widget::dealtimer()
{
static int i = 0;
i++;
/*设置LCD*/
ui->lcdNumber->display(i);

}

/*当界面有过于复杂的数据处理的时候需要用到多线程
避免界面卡死*/
void Widget::on_Lcd_Start_clicked()
{
/*如果定时器没有工作*/
if(MyTimer->isActive() == false)
{
MyTimer->start(100);
}
/*非常复杂的数据处理 耗时较长*/
//QThread::sleep(5);
thread->start();

/*处理完数据关闭定时器*/
//MyTimer->stop();
}
/*线程处理函数指向完成后发送的信号处理*/
void Widget::dealthread()
{
/*关闭定时器*/
MyTimer->stop();
qDebug() << "处理完毕";
}

void Widget::stopthread()
{

thread->quit();
/*等待线程处理完事情之后*/
thread->wait();
}
void terminate()
/*此函数直接关闭线程不等待线程任务结束*/
void quit()
/*此函数等待线程任务结束之后关闭线程*/

方法二:(QT5)

QT学习第六天_多线程_02

1.自定义一个类,只需要继承QObject即可,并且线程处理函数名字随便取,但是也只有一个线程处理函数

2.创建一个自定义线程类的对象,不能指定父对象

3.创建一个QThread类的对象,可以指定父对象

4.将自定义线程对象加入到QThread类的对象使用

5.启动线程的时候要注意:启动QThread类的对象线程,调用start函数只是启动了线程,但是没有开启线程处理函数,线程处理函数的开启需要用到信号槽机制。

6.关闭线程

/******************mythread.h*************************************/
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QThread>
#include <QDebug>

class mythread : public QObject
{
Q_OBJECT
public:
explicit mythread(QObject *parent = nullptr);

void thread();
void setflag(bool flag = true);
signals:
void mysignal();

private:
bool isStop;


};

#endif // MYTHREAD_H
/***************************mythread.cpp*********************************************/
#include "mythread.h"

mythread::mythread(QObject *parent) : QObject(parent)
{
isStop = false;
}
void mythread::thread()
{

while (!isStop)
{
QThread::sleep(1);
emit mysignal();
qDebug() << "子线程号:" << QThread::currentThread();
if(isStop) break;
}

}

void mythread::setflag(bool flag)
{
isStop = flag;
}
/******************************mywidgt.h************************************************/
#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include <mythread.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE

class MyWidget : public QWidget
{
Q_OBJECT

public:
MyWidget(QWidget *parent = nullptr);
~MyWidget();

mythread *testthread;
QThread *thread;
signals:
/*启动子线程的信号*/
void startsignal();

private slots:
void on_startPushbutton_clicked();
void delsignals();
void on_closePushbutton_clicked();
void dealclose();
private:
Ui::MyWidget *ui;
};
#endif // MYWIDGET_H
/************************************mywidgt.cpp*********************************************/
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QThread>

MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyWidget)
{
ui->setupUi(this);
/*动态分配空间不能知道父对象*/
testthread = new mythread();

/*创建子线程*/
thread = new QThread(this);

/*把自定义线程加入到子线程中*/
testthread->moveToThread(thread);

connect(testthread, &mythread::mysignal, this, &MyWidget::delsignals);
qDebug() << "主线程号:" << QThread::currentThread();
connect(this, &MyWidget::startsignal, testthread, &mythread::thread);

connect(this, &MyWidget::destroyed, this, &MyWidget::dealclose);
/*线程函数内部不允许操作图形界面 一般用数据处理
connect第五个参数作用:
只有在多线程的时候采意义 连接方式 直接 队列
默认的时候如果是多线程则使用队列
单线程使用直接方式
队列和直接差别:
队列:槽函数所在的线程和接受者一样
直接:槽函数所在线程和发送者一样
*/
}

MyWidget::~MyWidget()
{
delete ui;
}


void MyWidget::on_startPushbutton_clicked()
{
if(thread->isRunning() == true)
{
return;
}
/*启动线程但是没有启动线程处理函数*/
thread->start();
/*不能直接调用线程处理函数 直接调用导致线程处理函数和主线程处于同一线程*/
emit startsignal();
}

void MyWidget::delsignals()
{
static int i = 0;
i ++;
ui->lcdNumber->display(i);

}

void MyWidget::on_closePushbutton_clicked()
{
if(thread->isRunning() == false)
{
return ;
}

testthread->setflag();
thread->quit();
thread->wait();

}

void MyWidget::dealclose()
{
/* 释放对象*/
delete testthread;
on_closePushbutton_clicked();
}
QT学习第六天_qt_03
  • 收藏
  • 评论
  • 分享
  • 举报

上一篇:Qt学习第五天


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK