Qt5多线程编程的实现

Qt5多线程编程的实现

为什么需要多线程

在程序运行时,为了保证其正常运行及良好的用户体验,需要避免阻塞UI线程。如果所有操作都在UI线程中执行,当需要执行比较耗时或无法预知执行时间的操作时(比如下载文件、读写磁盘等),程序会出现“卡住”的状况,导致用户无法继续进行操作,程序表现为假死状态,影响用户使用体验。

Qt5多线程编程实现

在Qt5中,多线程编程的实现主要由以下几个部分组成:

QThread

QThread是一个基础的多线程类,继承自QObject。在使用时,需要自定义一个继承自QThread的子类,并重写run()函数,该函数代表子线程的执行任务。创建子线程的一般方式为创建QThread的子类对象,并使用start()函数启动run()函数执行线程任务。

// 继承QThread实现子线程
class Thread : public QThread {
public:
    void run() override {
        // 子线程任务执行代码
    }
};

// 创建并启动子线程
Thread* thread = new Thread();
thread->start();

QRunnable

QRunnable是一个轻量级的多线程类,继承自QObject,并实现了run()函数,当该类被传递给QThreadPool时,QThreadPool将自动创建一个线程并执行run()函数中的任务。创建QRunnable的一般方式是创建一个继承自QRunnable的子类,并实现run()函数。

// 继承自QRunnable实现轻量级的子线程任务
class Runnable : public QRunnable {
public:
    void run() override {
        // 子线程任务执行代码
    }
};

// 创建线程池并添加任务
QThreadPool* pool = QThreadPool::globalInstance();
Runnable* runnable = new Runnable();
pool->start(runnable);

QtConcurrent

QtConcurrent是一个较为高级的多线程类,提供了一些现成的多线程操作符,比如map()、filter()等,可以极大地简化多线程操作。和QRunnable一样,QtConcurrent也是一个轻量级的多线程类,调用时只需要传入要执行的函数及参数即可。

// QtConcurrent操作,map操作将vector中的元素进行平方,并使用QFuture等待返回
QFuture <int> future = QtConcurrent::map(vector, [](int& element) {
    return element * element;
});
future.waitForFinished();

QThreadPool

QThreadPool是多线程框架中的线程池,它可以管理线程的创建、关闭、等待及状态报告等。在使用时,需要先创建QThreadPool对象,并使用start()函数添加任务,QThreadPool会根据任务及系统性能自动管理线程数量。一旦线程池启用,只有在指定条件(如线程Idle时间、最大线程数等)下才停止工作。

// 创建线程池并添加任务
QThreadPool* pool = QThreadPool::globalInstance();
pool->setMaxThreadCount(4);
for (int i = 0; i < count; ++
    Runnable* runnable = new Runnable();
    pool->start(runnable);
}
pool->waitForDone();

示例说明

示例一:QThread实现

下面是一个使用QThread实现多线程的示例,包括主线程和子线程间的信号与槽传递。

// MyThread.h
class MyThread : public QThread {
Q_OBJECT
public:
    MyThread(QObject* parent = nullptr);
signals:
    void numberChanged(int number);
protected:
    void run() override;
};

// MyThread.cpp
MyThread::MyThread(QObject* parent)
    : QThread(parent){
}

void MyThread::run() {
    int number = 0;
    while(number < 100) {
        emit numberChanged(number);
        QThread::msleep(100);
        ++number;
    }
}

// MainWindow.h
class MainWindow : public QMainWindow {
Q_OBJECT
public:
    explicit MainWindow(QWidget* parent = nullptr);
public slots:
    void numberReceived(int number);
private slots:
    void on_pushButton_clicked();
private:
    Ui::MainWindow* ui;
    MyThread* m_thread;
};

// MainWindow.cpp
MainWindow::MainWindow(QWidget* parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow) {
    ui->setupUi(this);
    m_thread = new MyThread(this);
    connect(m_thread, &MyThread::numberChanged, this, &MainWindow::numberReceived);
}

void MainWindow::numberReceived(int number) {
    ui->lineEdit->setText(QString::number(number));
}

void MainWindow::on_pushButton_clicked() {
    if(!m_thread->isRunning()) {
        m_thread->start();
    }
}

在该示例中,主线程界面中有一个push button和一个line edit,当用户点击push button时,将在子线程中启动任务,并每100ms向主线程信号槽传递数字。主线程接收到信号后,将数字显示到line edit控件中。

示例二:QtConcurrent实现

下面是一个使用QtConcurrent实现多线程的示例,实现了一个简单的对文件夹进行递归遍历并计算其所有文件大小的程序。

// DirSizeCalculator.h
class DirSizeCalculator {
public:
    qint64 getDirSize(const QString& path);
private:
    static qint64 countSize(const QString& path);
};

// DirSizeCalculator.cpp
qint64 DirSizeCalculator::getDirSize(const QString& path) {
    QFuture <qint64> future = QtConcurrent::run(&countSize, path);
    future.waitForFinished();
    return future.result();
}

qint64 DirSizeCalculator::countSize(const QString& path) {
    qint64 size = 0;
    QFileInfo file_info(path);
    if(file_info.isFile()) {
        size += file_info.size();
    } else if(file_info.isDir()) {
        QDir dir(path);
        QStringList names = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot);
        foreach(QString name, names) {
            QString full_path = QDir(path).filePath(name);
            if(QFileInfo(full_path).isFile()) {
                size += QFileInfo(full_path).size();
            } else if(QFileInfo(full_path).isDir()) {
                size += countSize(full_path);
            }
        }
    }
    return size;
}

// MainWindow.h
class MainWindow : public QMainWindow {
Q_OBJECT
public:
    explicit MainWindow(QWidget* parent = nullptr);
private slots:
    void on_pushButton_clicked();
private:
    Ui::MainWindow* ui;
    DirSizeCalculator* m_calculator;
};

// MainWindow.cpp
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow) {
    ui->setupUi(this);
    m_calculator = new DirSizeCalculator();
}

void MainWindow::on_pushButton_clicked() {
    QString folder_path = QFileDialog::getExistingDirectory(this, "Please select folder");
    qint64 size = m_calculator->getDirSize(folder_path);
    QString display_text = QString("Folder size: %1 Bytes").arg(size);
    ui->label->setText(display_text);
}

在该示例中,主线程界面中有一个push button和一个label,当用户点击push button时,将弹出文件夹选择对话框,当用户选择文件夹后,程序将启动QtConcurrent的线程执行DirSizeCalculator中的 getDirSize() 函数,并等待线程返回结果,线程会利用递归方式计算文件夹下所有文件的大小,并将结果返回给主线程进行显示。

总结

多线程编程的实现在Qt5中非常便捷,开发者可以根据实际需求选择合适的多线程类,避免UI线程阻塞,降低程序出现假死等异常情况的概率,提高程序可靠性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Qt5多线程编程的实现 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • Java中线程的基本方法使用技巧

    Java中线程的基本方法使用技巧 1. 线程的创建 Java语言支持线程的创建,常用的线程创建方式有两种:继承Thread类与实现Runnable接口。 1.1 继承Thread类 继承Thread类是一种比较直接的方式,只需要重写Thread类的run()方法即可实现线程的创建。 class MyThread extends Thread { public…

    多线程 2023年5月16日
    00
  • Java Semaphore实现高并发场景下的流量控制

    Java Semaphore实现高并发场景下的流量控制 Semaphore是Java Concurrency API中一个用于实现流量控制的工具类。它可以控制同一时间请求某项资源的线程数量,以达到限流的效果。本文将详细介绍Semaphore的用法以及如何在高并发场景下使用它进行流量控制。 Semaphore的使用 Semaphore的创建: Semaphor…

    多线程 2023年5月16日
    00
  • JavaScript如何利用Promise控制并发请求个数

    如果我们需要在JavaScript中同时发起多个异步请求,我们可以通过使用Promise.all来实现并发处理。但是,如果我们的请求数量非常庞大,我们可能需要控制并发请求数量,以避免对系统造成过度的压力。下面是一些如何使用Promise来控制并发请求个数的技巧。 控制并发请求个数的方法 限制最大并发数 我们可以使用一个计数器和一个for或者while循环来实…

    多线程 2023年5月16日
    00
  • Promise面试题详解之控制并发

    控制并发是 Promise 中比较重要、也比较常见的使用场景之一。 那么在面试中可能会有关于此方面的题目,下面我们来详细讲解一下控制并发的面试题攻略。 什么是并发控制? 并发控制指的是对于某些需要进行并发处理的操作,保证其并发数量的控制。 举个例子,假设我们现在需要爬取若干个网页,但是为了对目标网站造成压力使用单线程轮流爬取的策略并不可取,这时我们就可以用 …

    多线程 2023年5月16日
    00
  • vbs 多线程下载实现代码

    前言 VBScript(简称VBS)是一种用来执行Microsoft Windows操作系统中自动化任务的脚本语言。在线上环境中,我们可能需要使用VBS进行多线程下载,以提高下载速度并减少等待时间。在本文中,我们将详细讲解如何使用VBS实现多线程下载。 步骤 1.首先,我们需要创建一个VBS文件,并在文件中引用WinHTTP对象。代码如下: Set http…

    多线程 2023年5月16日
    00
  • 使用java的HttpClient实现多线程并发

    使用Java的HttpClient实现多线程并发,包括以下步骤: 1.导入HttpClient库 使用HttpClient进行请求需要导入相应的库,具体可以使用Maven或直接下载jar包导入。 2.创建HttpClient对象 创建HttpClient对象用于发送请求。可以使用HttpClientBuilder类的build方法创建HttpClient对象…

    多线程 2023年5月16日
    00
  • Java并发中死锁、活锁和饥饿是什么意思

    Java并发中死锁、活锁和饥饿是什么意思 在Java并发编程中,我们会遇到三种常见的场景:死锁、活锁和饥饿。这三种场景都是由于多个线程访问共享资源而引发的问题。下面我将详细讲解这三种情况的具体含义和示例。 死锁 在多线程编程过程中,我们常常会使用synchronized关键字来保证同一个时间只有一个线程可以访问一段代码。而死锁则是因为多个线程在访问多个共享资…

    多线程 2023年5月16日
    00
  • 多线程如何解决for循环效率的问题

    作为一种并发编程方式,多线程可以有效提高程序的执行效率,并解决“for循环效率低”的问题。下面将详细讲解多线程如何解决for循环效率问题的攻略。 首先,明确for循环的效率低问题。在for循环中,由于代码是顺序执行的,每次执行完一个循环体才会进入下一个循环体,因此在循环次数较大的情况下,会造成程序执行速度慢的问题。 使用多线程可以解决for循环效率低的问题。…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部