QT线程优势:gui线程+子线程
使用多线程有什么好处?
1.提高应用程序的响应速度。这对于开发图形界面程序尤其重要,当一个操作耗时很长时
(比如大批量I/O或大量矩阵变换等CPU密集操作),整个系统都会等待这个操作,
程序就不能响应键盘、鼠标、菜单等操作,而使用多线程技术可将耗时长的操作置于一个新的线程,
从而不会影响到主GUI线程,从而避免上述问题。
2.使多CPU系统更加有效:当线程数不大于CPU核数时,
操作系统可以调度不同的线程运行于不同的CPU核上。
3.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,
成为独立或半独立的运行部分,这样有利于程序的理解和维护。
实例:创建一个界面,界面show以后调用睡眠函数,观察效果。
QThread
1.接口
Public Functions
QThread(QObject *parent = 0) //构造函数
bool isFinished() const //判断线程是否退出
bool wait(unsigned long time = ULONG_MAX)
//等待某个线程结束,最多等待time ms,如果时间没有设置,
那么永远等待。
Public Slots
void start(Priority priority = InheritPriority) //启动线程必须使用start
void terminate()–>杀死线程
Static Public Members
Qt::HANDLE currentThreadId() [static] //得到当前执行者线程ID,可以直接qDebug
void msleep(unsigned long msecs) [static]
void sleep(unsigned long secs) [static]
void usleep(unsigned long usecs) [static]
睡眠函数不能在主线程调用,会造成界面卡死。
Protected Functions
virtual void run(); //启动新线程不能直接调用run,需要调用start接口,start会启动新线程,然后执行run里的代码块。
线程开发需要思考点:
1.创建线程
2.如何给子线程传参:通过参数设置(子线程对象提供相应接口,主线程调用)
3.子线程如何和主线程对象通信
2.编程流程
a. 子类化QThread:重写一个类,继承自QThread
b. 重写 run 函数,run函数内有一个 while 或 for 的循环:执行耗时操作
c. 主线程调用start方法开始子线程
d. 设置一个标记为来控制循环的退出,或者父线程调用terminate停止子线程
e. 设置必要的信号和槽做连接
3.编程实例:
重写一个线程子类,GUI线程传递一个值给子线程。
界面放置一个按钮,点击按钮后线程开始运行。
线程打印主线程传递值以后睡眠5s,线程运行结束后
通知主线程,主线程打印提示。
4.编程实例:
在线程增加一个槽函数,界面部件触发这个槽函数,验证槽函数执行
在GUI线程还是子线程。
结论:所有耗时操作都应该封装到run方法中,并由主线程start()来运行。
其它的所有方法,包括槽函数,即使属于线程类,如果被主线程直接调用,
也是在GUI线程中执行。
5.子线程不允许有界面逻辑的操作,所有界面操作都应该由GUI主线程来进行。
6.编程实例:
改进文件拷贝程序
#include "copywig.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CopyWig w;
w.show();
return a.exec();
}
#ifndef COPYWIG_H
#define COPYWIG_H
#include <QtWidgets>
#include "copythread.h"
namespace Ui {
class CopyWig;
}
class CopyWig : public QWidget
{
Q_OBJECT
public:
explicit CopyWig(QWidget *parent = 0);
~CopyWig();
private slots:
void on_copyBtn_clicked();
void updatePro(int);
private:
Ui::CopyWig *ui;
CopyThread *cpTh;
};
#endif // COPYWIG_H
#include "copywig.h"
#include "ui_copywig.h"
CopyWig::CopyWig(QWidget *parent) :
QWidget(parent),
ui(new Ui::CopyWig)
{
ui->setupUi(this);
ui->progressBar->setValue(0);
cpTh = new CopyThread(this);
QObject::connect(cpTh, SIGNAL(curProval(int)),
this, SLOT(updatePro(int)));
}
CopyWig::~CopyWig()
{
delete ui;
}
/**
* @brief CopyWig::on_copyBtn_clicked
1.选择源文件和目标文件
2.传递两个文件名给子线程
3.启动子线程
*/
void CopyWig::on_copyBtn_clicked()
{
QString srcName = QFileDialog::getOpenFileName(this, tr("Open File"),
"d:",
tr("All (*.*)"));
ui->srcEdit->setText(srcName);
QString dstName = QFileDialog::getSaveFileName(this, tr("Save File"),
"d:/untitled",
tr("All (*.*)"));
ui->dstEdit->setText(dstName);
cpTh->setSrcName(srcName);
cpTh->setDstName(dstName);
cpTh->start();
}
void CopyWig::updatePro(int val)
{
ui->progressBar->setValue(val);
}
#ifndef COPYTHREAD_H
#define COPYTHREAD_H
#include <QThread>
#include <QFile>
#include <QDebug>
#include <QFileInfo>
class CopyThread : public QThread
{
Q_OBJECT
public:
explicit CopyThread(QObject *parent = 0);
void setSrcName(const QString &srcName){this->srcName = srcName;}
void setDstName(const QString &dstName){this->dstName = dstName;}
signals:
void curProval(int);
public slots:
protected:
void run();
QString srcName;
QString dstName;
};
#endif // COPYTHREAD_H
#include "copythread.h"
CopyThread::CopyThread(QObject *parent) :
QThread(parent)
{
}
/**
* @brief CopyThread::run
* 1.从主线程获取源文件和目标文件的名字
* 2.进行拷贝操作
* 3.把当前拷贝进度回传给主线程
*/
void CopyThread::run()
{
//复制操作:把源文件分解成帧,然后写入目标文件
QFile srcFile(srcName);
if(!srcFile.open(QIODevice::ReadOnly)){
qDebug()<<"open src file failed";
}
QFile dstFile(dstName);
if(!dstFile.open(QIODevice::WriteOnly | QIODevice::Truncate)){
qDebug()<<"open src file failed";
}
QFileInfo info(srcName);
qint64 totalSize = info.size();
qint64 copySize = 0;
int proVal = 0;
QByteArray buf;
while (!srcFile.atEnd()) {
buf = srcFile.read(1024); //1024 Byte
dstFile.write(buf);
copySize += 1024;
proVal = 100 * copySize / totalSize;
//通过信号把当前拷贝进度值传递给主线程
emit curProval(proVal);
}
}