QT学习第六天

一、为什么使用线程

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

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

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

  • 使多CPU系统更加有效。

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

  • 改善程序结构。

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

二、线程的特点

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

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

三、线程管理

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

方法一:(QT4)

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学习第六天

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();
}
发表评论

相关文章