13.线程基础

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);
    }
}

发布了10 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40083589/article/details/94136821