Qt5.14.2 Qt多线程实战演练,全面掌握线程同步和线程池最佳实践

这篇具有很好参考价值的文章主要介绍了Qt5.14.2 Qt多线程实战演练,全面掌握线程同步和线程池最佳实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


多线程编程是每个开发者必须掌握的基本能力之一。在上一篇文章中,我们学习了Qt多线程编程的理论知识。本文将切入实战,提供多个案例代码,帮助你彻底掌握Qt的多线程编程实践技巧。


案例1: 使用QThread执行耗时任务

这个案例演示了如何通过继承QThread和重写run()函数,在子线程中执行耗时操作,避免阻塞UI线程。

// WorkerThread.h
#include <QThread>

class WorkerThread : public QThread
{
    Q_OBJECT

public:
    WorkerThread() {}
    
protected:
    void run() override {
        // 耗时操作
        for(int i=0; i<1000000000; ++i) {
            // 一些复杂的计算
        }
        
        // 通知UI线程任务完成
        emit finished();
    }
    
signals:
    void finished();
};

// main.cpp
#include <QApplication>
#include "WorkerThread.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    
    // 创建工作线程
    WorkerThread* worker = new WorkerThread;
    
    // 连接信号与槽
    QObject::connect(worker, &WorkerThread::finished, [&](){
        qDebug() << "Task finished";
    });
    
    // 启动线程
    worker->start();
    
    w.show();
    return a.exec();
}

在这个例子中,我们定义了WorkerThread类继承自QThread, 并在run()函数中执行模拟的耗时操作。当耗时操作完成后,会发出finished()信号通知UI线程。在主线程中,我们创建WorkerThread实例并启动线程,连接finished()信号到一个Lambda表达式,这样就能在UI线程中安全地更新界面状态。


案例2: 使用QThreadPool执行批量任务

下面这个示例展示了如何利用QThreadPool快速并行执行大量的小任务,充分利用CPU的多核特性。

// WorkerTask.h 
#include <QRunnable>

class WorkerTask : public QRunnable
{
public:
    WorkerTask(int num) : m_num(num) {}
    
    void run() override {
        // 模拟耗时操作
        for(int i=0; i<1000000; ++i) {
            // ...
        }
        
        qDebug() << "Task" << m_num << "completed in thread" << QThread::currentThreadId();
    }
    
private:
    int m_num;
};

// main.cpp
#include <QThreadPool>
#include "WorkerTask.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    // 获取线程池实例
    QThreadPool* pool = QThreadPool::globalInstance();
    
    // 设置最大线程数为理想值
    pool->setMaxThreadCount(QThread::idealThreadCount());
    
    // 投递大量小任务
    for(int i=0; i<1000; ++i) {
        WorkerTask* task = new WorkerTask(i);
        pool->start(task);
    }
    
    return a.exec();
}

在这个例子中,我们定义了WorkerTask类继承自QRunnable,它的run()函数用于模拟耗时任务。在主线程中,我们首先获取了QThreadPool的全局实例,设置最大线程数为系统理想线程数,然后循环构建1000个WorkerTask实例并通过pool->start()投递给线程池并行执行。

由于使用了线程池避免了频繁创建销毁线程的开销,所有任务能够较为高效地利用系统多核资源并行执行。当线程池中所有线程都处于繁忙状态时,新投递的任务会进入队列排队等待,所以我们要注意控制队列长度,避免性能反而下降。


案例3: 任务队列控制示例

这个示例演示了如何通过监控线程池的任务队列长度,对任务投递策略进行动态调整,防止队列过长导致性能下降。

// ThreadPoolManager.h
#include <QThreadPool>

class ThreadPoolManager : public QObject
{
    Q_OBJECT
    
public:
    static ThreadPoolManager* instance(); 
    
    QThreadPool* threadPool() const { return m_threadPool; }
    
    void enqueueTask(QRunnable* task, bool isHighPriority=false);
    
private:
    explicit ThreadPoolManager(QObject* parent=nullptr);
    ~ThreadPoolManager();
    
    QThreadPool* m_threadPool;
    int m_maxThreadCount;
};

// ThreadPoolManager.cpp
#include "ThreadPoolManager.h"

ThreadPoolManager* ThreadPoolManager::instance()
{
    static ThreadPoolManager manager;
    return &manager;
}

ThreadPoolManager::ThreadPoolManager(QObject* parent) 
    : QObject(parent), 
      m_threadPool(QThreadPool::globalInstance()),
      m_maxThreadCount(QThread::idealThreadCount())
{
    m_threadPool->setMaxThreadCount(m_maxThreadCount);
}

void ThreadPoolManager::enqueueTask(QRunnable* task, bool isHighPriority)
{
    // 如果队列过长,根据任务优先级调整策略
    if (m_threadPool->activeThreadCount() == m_maxThreadCount && 
        m_threadPool->queueLength() > 1000) {
        
        if (isHighPriority) { // 对高优先级任务保留线程 
            m_threadPool->reserveThread();
        } else { // 对低优先级任务延迟投递
            QTimer::singleShot(50, [=]{ 
                enqueueTask(task, false); 
            });
            return;
        }
    }
    
    // 投递任务
    m_threadPool->start(task);
    
    // 根据队列长度动态调整线程池大小
    int qLen = m_threadPool->queueLength();
    if (qLen > 2000) {
        m_threadPool->setMaxThreadCount(m_maxThreadCount + 2);
    } else if (qLen < 500) {
        m_threadPool->setMaxThreadCount(m_maxThreadCount);
    }
}

// 在其他地方使用
#include <QThreadPool>
#include "ThreadPoolManager.h"

WorkerTask* task = new WorkerTask;
ThreadPoolManager::instance()->enqueueTask(task, isHighPriority);

在这个例子中,我们定义了ThreadPoolManager类对QThreadPool的使用进行了封装。enqueueTask()函数在投递任务前会先检查当前线程池的状态,如果所有线程都繁忙且任务队列已经积压超过一定长度,那么就会对高优先级任务调用reserveThread()保留线程资源,对低优先级任务延迟50毫秒后重新投递。


此外,enqueueTask()还会根据任务队列的长度动态调整线程池的最大线程数,以应对突发的任务高峰。当队列长度超过2000时,最大线程数会增加;当队列长度降至500以下。


案例4: 使用QMutex保护临界区

这个例子演示了如何使用QMutex互斥锁来保护一段临界区代码,防止多个线程同时访问。

// SharedData.h
#include <QMutex>

class SharedData : public QObject
{
    Q_OBJECT

public:
    SharedData() : m_value(0) {}

    void increase()
    {
        // 获取互斥锁
        QMutexLocker locker(&m_mutex);
        
        // 临界区
        m_value++;
    }

    int value() const
    {
        // 由于只有读操作,无需加锁
        return m_value;
    }

private:
    QMutex m_mutex;
    int m_value;
};

// Worker.h
#include <QThread>
#include "SharedData.h"

class Worker : public QThread
{
    Q_OBJECT

public:
    Worker(SharedData* data) : m_data(data) {}

protected:
    void run() override
    {
        // 每个线程执行100000次增加操作
        for(int i=0; i<100000; ++i)
            m_data->increase();
    }

private:
    SharedData* m_data;
};

// main.cpp
#include <QCoreApplication>
#include "Worker.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    SharedData data;

    // 创建10个工作线程
    QList<QThread*> threads;
    for(int i=0; i<10; ++i)
    {
        Worker* worker = new Worker(&data);
        worker->start();
        threads.append(worker);
    }

    // 等待所有线程执行完毕
    for(QThread* thread : threads)
        thread->wait();

    qDebug() << "Final value:" << data.value(); // 输出 1000000

    return a.exec();
}

在这个例子中,我们定义了SharedData类,它有一个整型value成员变量和一个QMutex互斥锁m_mutex。increase()函数用于对value进行累加,在执行累加操作的临界区代码前,通过QMutexLocker自动获取互斥锁,退出临界区时自动释放锁。

Worker类继承自QThread,在run()函数中循环100000次调用SharedData::increase()函数。


在主线程中,我们创建了一个SharedData实例,然后创建10个Worker线程共享这个实例。所有线程执行完成后,我们输出SharedData的value值,由于临界区被很好地保护,最终输出的结果是正确的1000000。

如果不使用互斥锁,那么多个线程同时访问value变量,就会出现"丢失更新"的问题,导致最终结果不正确。


案例5: 使用QReadWriteLock实现读写分离

对于有读写分离需求的场景,我们可以使用QReadWriteLock来进一步提高并发性能。下面的例子演示了如何使用QReadWriteLock。

// Cache.h  
#include <QReadWriteLock>
#include <QHash>

template<typename Key, typename Value>
class Cache
{
public:
    bool insert(const Key& key, const Value& value)
    {
        QWriteLocker locker(&m_lock); // 写锁
        m_cache.insert(key, value);
        return true;
    }
    
    bool getValue(const Key& key, Value& value)
    {
        QReadLocker locker(&m_lock); // 读锁
        auto it = m_cache.find(key);
        if (it == m_cache.end())
            return false;
        value = it.value();
        return true;
    }

private:
    QHash<Key, Value> m_cache;
    QReadWriteLock m_lock;
};

// 使用示例
Cache<QString, int> cache;
cache.insert("apple", 5);
cache.insert("banana", 8);

int value;
if (cache.getValue("apple", value))
    qDebug() << value; // 输出5

在这个例子中,我们定义了一个简单的键值对缓存Cache类。它内部使用QHash存储数据,使用QReadWriteLock控制数据访问。

  • 写操作insert()使用QWriteLocker获取独占写锁,确保线程安全地插入或修改数据。
  • 读操作getValue()只需使用QReadLocker获取共享读锁,因为只读不修改数据,所以允许多个线程同时读取。

使用读写锁的主要优势是,在存在并发读取的情况下,能够最大限度地提高并发性能。多个读线程可以同时访问共享数据而无需等待,只有存在写操作时才会阻塞所有读写线程。


这种细粒度的锁机制可以很好地应用于读多写少的场景,充分利用多核CPU的计算能力,比如数据缓存、文件缓存等领域。开发者可以根据实际需求,权衡性能与线程安全,合理选择不同的线程同步原语。


通过这几个实战案例,相信你已经对Qt的多线程编程有了更深入的理解。无论是创建工作线程执行耗时任务,还是使用线程池高效管理异步任务,亦或是用锁机制保护共享数据,Qt为我们提供了全面的多线程支持。掌握了这些技能,就能更好地开发出高性能、稳定可靠的应用程序。

文章来源地址https://www.toymoban.com/news/detail-841824.html

到了这里,关于Qt5.14.2 Qt多线程实战演练,全面掌握线程同步和线程池最佳实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt5.14.2安装教程和VS2019中的qt环境配置

    Qt(官方发音 [kju:t],音同 cute)是一个跨平台的 C++ 开发库,主要用来开发图形用户界面(Graphical User Interface,GUI)程序,当然也可以开发不带界面的命令行(Command User Interface,CUI)程序。 Qt 还存在 Python、Ruby、Perl 等脚本语言的绑定, 也就是说可以使用脚本语言开发基于

    2024年02月07日
    浏览(51)
  • qt5.14.2 独立msvc环境搭建(不安装vs)

    一般情况下msvc下的qt开发,无论你是用qtcreator还是vs都推荐安装对应vs版本,这样是最省事和便捷的,但在有些情况下不便安装vs但项目又需要使用msvc,则可以只安装msvc c++编译器,步骤如下: 备注:文内用到所有包均上传到不限速的阿里云盘,欢迎关注 阿里云盘分享 https:

    2024年02月04日
    浏览(50)
  • QT5.14.2安卓环境配置及创建程序在手机上运行

    主要参考:Qt | Qt For Android、Qt5.14.2安卓开发环境搭建详细步骤_哔哩哔哩_bilibili            Qt | Qt For Android、Qt5.14.2安卓开发环境搭建详细步骤_qt安卓开发环境搭建-CSDN博客 出错时参考:如何在Windows下搭建Qt安卓开发环境? - 知乎 (zhihu.com)                 百度网盘链接 需要

    2024年04月17日
    浏览(38)
  • Qt | Qt For Android、Qt5.14.2安卓开发环境搭建详细步骤

    1、简介 搭建Qt For Android开发环境需要安装的软件有: JAVA SDK Android SDK Android NDK 其中Android NDK直接下载的压缩包,解压即可。 Android SDK提供了安装包和压缩包,用哪个都行。 Qt For Android开发环境的搭建说简单也简单,说复杂也比较复杂。说它简单是因为安装的软件就一两个,安

    2024年02月02日
    浏览(53)
  • QT5.14.2 + VS2019 + VTK8.2.0配置环境

    1.1.1 软件下载地址:Index of /archive/qt 1.1.2 安装的时候需要注意: 安装时候至少把MSVC2017 64bit选择上。 用VS2019时候,打开Qt Creator,点击工具-选项,构建套件中可能会显示黄色或者红色感叹号,这个时候就需要额外的配置。  打开VS2019,点击顶部工具 - 获取工具和功能 - 在跳出的

    2024年02月06日
    浏览(71)
  • Windows+Qt5.14.2+android x86配置与处理adb报错

    资源下载 可在部分国内镜像源下载Qt5.14.2:Index of /qt/archive/qt/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 其他工具 android studio:下载 Android Studio 和应用工具 - Android 开发者  |  Android Developers sdk manager 、ndk、java 安装过程 根据qt5.14.2自助选择需要安装的anroid组件等、vs2

    2024年01月20日
    浏览(42)
  • VS2022+qt5.14.2配置qcustomplot2.1.16(坑很多)

    QCustomPlot 是一个超强超小巧的qt绘图类,非常漂亮,非常易用,只需要加入一个qcustomplot.h和qcustomplot.cpp文件即可使用。 官方网址:https://www.qcustomplot.com/ 源码下载地址:https://www.qcustomplot.com/index.php/download 将qcustomplot.h和qcustomplot.cpp2个文件添加到QT项目,如下: 在项目属性Qt

    2024年02月13日
    浏览(51)
  • 在Visual Studio 2019中使用Qt5.14.2并配置相关路径(含opencv/halcon)联合编程配置

    (1)Qt下载安装 参考链接:https://blog.csdn.net/weixin_41977337/article/details/106859728 1、QT下载 推荐下载Qt5.12.3版本(5.12为LTS长期支持版本),官方下载链接: http://download.qt.io/archive/qt/. 清华源连接: https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/5.14/5.14.2/ 版本5.14.2下载: 断网即可跳过注册账

    2024年02月04日
    浏览(48)
  • Linux Server 20.04 Qt5.14.2配置Jetson Orin Nano Developer Kit 交叉编译环境

    最近公司给了我一块Jetson Orin Nano的板子,先刷了系统(1.Jetson Orin Nano Developer Kit系统刷机)又让我搭建交叉编译环境,所以有了下面的文章 1.1设备环境 1.1.1 Server: 1.1.2 Jetson Orin Nano : 1.2 Qt相关下载地址 1.2.1 Qt源码下载 Qt源码下载(最好下载tar.xz,tar格式可能在windows下编辑过,后期

    2024年02月12日
    浏览(48)
  • Qt5.14和Qt5.15在线下载

    https://download.qt.io/archive/qt/5.14/5.14.2/ https://download.qt.io/official_releases/online_installers/ qt 安装太慢的问题如何解决? Qt学习 QT最新版本下载安装(QT5.15及QT6.X) 清华大学 Qt镜像下载中心

    2024年02月12日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包