QT多线程编程基础

文章目录

    • 线程基础
    • QT中的多线程技术
      • QThread
        • 派生QThread类对象的方法(重写Run函数)
        • 使用信号与槽方式来实现多线程
        • 注意
      • QThreadPool和QRunnable
        • QThreadPool类
        • QRunnable类
      • Qt Concurrent
        • QtConcurrent基本使用
      • 选择合适的方法
    • 参考

本文将对QT中的多线程编程进行介绍。

线程基础

线程是实现一个进程内并发的方式。
线程和进程的区别和联系可以参考:linux中进程,线程,协程

使用线程的场景:

  1. 利用多核处理
  2. 通过将长时间处理或阻塞调用转移到其他线程来保证主线程的响应

GUI线程和工作线程:
每个程序在启动时都有一个线程。这个线程被称为“主线程”(在Qt应用程序中也称为“GUI线程”)。Qt GUI必须在这个线程中运行。辅助线程通常被称为“工作线程”,因为它用于从主线程中卸载处理工作。

QT中线程的备选方案:

  • QEventLoop::processEvents()
    在耗时计算期间反复调用QEventLoop::processEvents()可防止图形用户界面(GUI)阻塞。然而,此解决方案扩展性不佳,因为根据硬件情况,对 processEvents () 的调用可能过于频繁或不够频繁。
  • QTimer
    使用定时器来安排在未来某个时间点执行槽函数有时可以方便地进行后台处理。间隔为 0 的定时器在没有更多事件要处理时会立即超时。
  • QSocketNotifier QNetworkAccessManager QIODevice::readyRead()
    这是一种替代方案,可替代拥有一个或多个线程,每个线程在慢速网络连接上进行阻塞式读取。只要对网络数据块的响应计算能够快速执行,这种响应式设计就比线程中的同步等待更好。响应式设计比线程化更不易出错且更节能。在许多情况下,还具有性能优势。

QT中的多线程技术

QThread

QThread是QT多线程的基础,QThread既可以直接实例化,也可以进行子类化。对QThread进行实例化可提供一个并行事件循环,允许在辅助线程中调用QObject的槽函数。对QThread进行子类化可使应用程序在启动其事件循环之前初始化新线程,或者在没有事件循环的情况下运行并行代码。
QThread常用API:

// QThread 类常用 API
// 构造函数
QThread::QThread(QObject *parent = Q_NULLPTR);
// 判断线程中的任务是不是处理完毕了
bool QThread::isFinished() const;
// 判断子线程是不是在执行任务
bool QThread::isRunning() const;

// Qt中的线程可以设置优先级
// 得到当前线程的优先级
Priority QThread::priority() const;
void QThread::setPriority(Priority priority);
优先级:
    QThread::IdlePriority        --> 最低的优先级
    QThread::LowestPriority
    QThread::LowPriority
    QThread::NormalPriority
    QThread::HighPriority
    QThread::HighestPriority
    QThread::TimeCriticalPriority
    QThread::InheritPriority    --> 最高的优先级, 默认是这个


// 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是
// 等待任务完成, 然后退出线程, 一般情况下会在 exit() 后边调用这个函数
bool QThread::wait(unsigned long time = ULONG_MAX);

QThread信号槽:

// 退出线程, 停止底层的事件循环
// 退出线程的工作函数
void QThread::exit(int returnCode = 0);
// 和调用 exit() 效果是一样的
// 代用这个函数之后, 再调用 wait() 函数
void QThread::quit();
// 启动子线程
void QThread::start(Priority priority = InheritPriority);
// 线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数
void QThread::terminate();

// 线程中执行的任务完成了, 发出该信号
// 任务函数中的处理逻辑执行完毕了
void QThread::finished();
// 开始工作之前发出这个信号, 一般不使用
void QThread::started();

QThread静态函数:

// Creates a new QThread object that will execute the function f with the arguments args.
QThread *QThread::create(Function &&f, Args &&... args)
// 返回一个指向管理当前执行线程的QThread的指针
QThread *QThread::currentThread();
Qt::HANDLE QThread::currentThreadId()
// 返回可以在系统上运行的理想线程数 == 和当前电脑的 CPU 核心数相同
int QThread::idealThreadCount();
// 线程休眠函数
void QThread::msleep(unsigned long msecs);    // 单位: 毫秒
void QThread::sleep(unsigned long secs);    // 单位: 秒
void QThread::usleep(unsigned long usecs);    // 单位: 微秒
// 放弃当前线程转到另外可执行的线程,有系统决定转到哪个线程。
void QThread::yieldCurrentThread()
派生QThread类对象的方法(重写Run函数)
  1. 自定义一个自己的类,使其继承自QThread类;
  2. 在自定义类中覆写QThread类中的虚函数run()。
#include <QThread>

class thread_demo : public QThread
{
    Q_OBJECT
public:
    thread_demo(QObject *parent = nullptr);
    ~thread_demo();

protected:
    void run() override;
};

#include "thread_demo.h"
#include <QDebug>

thread_demo::thread_demo(QObject *parent):QThread(parent)
{
    qInfo() << "create QThread in " << QThread::currentThreadId();
}

thread_demo::~thread_demo()
{
    qInfo() << "destroy QThread in " << QThread::currentThreadId();
}

void thread_demo::run()
{
    qInfo() << "Qthread run in " << QThread::currentThreadId();
    qDebug() << "开始执行线程";
    QThread::sleep(10);
    qDebug() << "线程结束";
}

子类化QThread的方法,就是重写了QThread中的run()函数,在run()函数中定义了需要的工作。这样的结果是,我们自定义的子线程调用start()函数后,便开始执行run()函数。如果在自定义的线程类中定义相关槽函数,那么这些槽函数不会由子类化的QThread自身事件循环所执行,而是由该子线程的拥有者所在线程(一般都是主线程)来执行。如果你不明白的话,请看,第二个例子中,子类化的线程的槽函数中输出当前线程的ID,而这个ID居然是主线程的ID!!事实的确是如此,子类化的QThread只能执行run()函数中的任务直到run()函数退出,而它的槽函数根本不会被自己的线程执行。

使用信号与槽方式来实现多线程

上面的方法存在一个局限性,只有一个run()函数能够在线程中去运行,但是当有多个函数在同一个线程中运行时,就没办法了,至少实现起来很麻烦。
使用信号与槽的方式,也就是把在线程中执行的函数(我们可以称之为线程函数)定义为一个槽函数。

  1. 创建一个新的类(demo_worker),让这个类从 QObject 派生,在这个类中添加一个公共的成员函数(slot_startwork),函数体就是我们要子线程中执行的业务逻辑
  2. 在主线程中创建一个 QThread 对象,这就是子线程的对象
  3. 在主线程中创建工作的类对象(千万不要指定给创建的对象指定父对象)
  4. 将 MyWork 对象移动到创建的子线程对象中,需要调用 QObject 类提供的 moveToThread() 方法
  5. 启动子线程,调用 start(), 这时候线程启动了,但是移动到线程中的对象并没有工作
  6. 通过信号触发调用 demo_worker 类对象的工作函数,让这个函数开始执行,这时候是在移动到的那个子线程中运行的
#include <QObject>

class demo_worker : public QObject
{
    Q_OBJECT
public:
    explicit demo_worker(QObject *parent = nullptr);

public slots:
    void slot_startwork();

signals:
    void signal_workdown();
};

#include "demo_worker.h"
#include <QDebug>
#include <QThread>

demo_worker::demo_worker(QObject *parent)
    : QObject{parent}
{
    qInfo() << "create demo worker in " << QThread::currentThreadId();
}

void demo_worker::slot_startwork()
{
    qInfo() << "start demo work " << QThread::currentThreadId();
    qDebug() << "开始执行线程";
    QThread::sleep(10);
    qDebug() << "线程结束";
    emit signal_workdown();
}

在主线程中:

qInfo() << "==================";
    work_thread = new QThread;
    worker = new demo_worker;
    connect(this, &demo_app::signal_start_work, worker, &demo_worker::slot_startwork);
    connect(worker, &demo_worker::signal_workdown, [](){
        qInfo() << "demo worker work down";
    });
    worker->moveToThread(work_thread);
    work_thread->start();

    emit signal_start_work();

moveToThread方法,是把我们需要的工作全部封装在一个类中,将每个任务定义为一个的槽函数,再建立触发这些槽的信号,然后把信号和槽连接起来,最后将这个类调用moveToThread方法交给一个QThread对象,再调用QThread的start()函数使其全权处理事件循环。于是,任何时候我们需要让线程执行某个任务,只需要发出对应的信号就可以。其优点是我们可以在一个worker类中定义很多个需要做的工作,然后发出触发的信号线程就可以执行。相比于子类化的QThread只能执行run()函数中的任务,moveToThread的方法中一个线程可以做很多不同的工作(只要发出任务的对应的信号即可)。

注意
  • 子线程中不能操作UI:Qt中子线程不能执行任何关于界面的处理,包括消息框的弹出。正确的操作应该是通过信号槽,将一些参数传递给主线程,让主线程(也就是Controller)去处理。
  • 自定义的QThread类不能指定父对象
  • 注意connect QThread时候的连接类型,它关系着槽函数在哪个线程

QThreadPool和QRunnable

为了避免频繁创建和销毁线程,可以使用QThreadPool复用线程。
要在QThreadPool的某个线程中运行代码,需要重新实现QRunnable::run() 并实例化子类化的QRunnable。使用QThreadPool::start() 将QRunnable放入QThreadPool的运行队列中。当有线程可用时,QRunnable::run() 中的代码将在该线程中执行。
每个 Qt 应用程序都有一个全局线程池,可以通过QThreadPool::globalInstance()进行访问。这个全局线程池会根据 CPU 的核心数量自动维持一个最佳的线程数量。然而,也可以显式地创建和管理一个单独的QThreadPool。

QThreadPool类

主要属性:
1、activeThreadCount: 此属性表示线程池中的活动线程数,通过activeThreadCount() 调用。
2、expiryTimeout: 线程活着的时间。没有设置expiryTimeout毫秒的线程会自动退出,此类线程将根据需要重新启动。默认的expiryTimeout为30000毫秒 (30 秒)。如果expiryTimeout为负, 则新创建的线程将不会过期, 在线程池被销毁之前, 它们将不会退出。通过expiryTimeout()调用,通setExpiryTimeout(int expiryTimeout)设置 。
3、maxThreadCount : int 表示线程池使用的最大线程数。
通过maxThreadCount() 调用,通过setMaxThreadCount(int maxThreadCount) 设置
注意:即使maxThreadCount限制为零或为负数, 线程池也至少有1个线程。

主要成员函数:
QThreadPool *QThreadPool::globalInstance()
返回Qt应用程序全局线程池实例。
void reserveThread()
预约一个线程,这个函数总是会增加活动线程的数量。这意味着通过使用这个函数,activeThreadCount()可以返回一个大于maxThreadCount()的值。
void releaseThread()
释放以前通过调用reserveThread()预约的线程。
如果不先预约一个线程,调用这个函数会临时增加maxThreadCount()。当线程进入休眠等待时,能够允许其他线程继续。
要记得在完成等待时调用reserveThread(),以便线程池可以正确控制activeThreadCount()。
void QThreadPool :: start(QRunnable * runnable,int priority = 0)
在任务数量小于maxThreadCount时,为每个runnable任务预约一个线程。超过maxThreadCount时,将任务放入运行队列中。priority 参数用来设置线程运行优先级。
void QThreadPool::start(std::function<void ()> functionToRun, int priority = 0)
保留一个线程来运行functionToRun
bool tryStart(QRunnable *runnable)
此方法尝试预约一个线程来运行runnable。如果在调用的时候没有线程可用,那么这个函数什么都不做,并返回false。否则,将使用一个可用线程立即运行runnable,并返回此函数true。
void clear()
用于删除在任务队列中,还没有启动的任务。
bool tryTake(QRunnable *runnable)
如果runnable任务还没开始运行,那么从队列中删除此runable任务,此时函数返回true;如果runnable任务已经运行,返回false。
只用来删除runnable->autoDelete() == false的runnable任务,否则可能会删错任务.
bool waitForDone(int msecs = -1)
等待msecs毫秒, 以便所有线程退出并从线程池中移除所有线程。如果删除了所有线程, 则返回true ,否则, 它将返回false。默认等待时间为-1,即等待最后一个线程退出。

总结:

  • QThreadPool 类管理 QRunnable /QThread 的集合。
  • QThreadPool 管理和回收单独的 QThread 对象,以减少使用线程的程序中的线程创建成本。
  • 每个 Qt 应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。
  • 要使用 QThreadPool,需要子类化 QRunnable 并实现 run() 虚函数。然后创建该类的对象并将其传递给 QThreadPool::start()。QThreadPool 默认自动删除 QRunnable。
  • QThreadPool 是管理线程的低级类,Qt Concurrent 模块是更高级的方案。
QRunnable类

QRunnable类是所有runable对象的基类。
QRunnable类是一个接口, 用于表示需要执行的任务或代码段, 具体任务在run() 函数内部实现。
可以使用QThreadPool在各个独立的线程中执行代码。如果autoDelete() 返回true (默认值), QThreadPool将自动删除QRunnable 。使用setAutoDelete() 可更改是否自动删除。
主要成员函数:
bool autoDelete() const
获取自动删除是否启用,启用返回true,未启用返回false。
virtual void run() = 0
纯虚函数,在QRunnable子类中实现详细任务处理逻辑。
void setAutoDelete(bool autoDelete)
如果autoDelete为 true, 则启用自动删除。否则自动删除将被禁用。
如果启用了自动删除, QThreadPool将在调用 run () 函数返回后自动删除此runable对象。否则, runable对象所有权不属于线程池,由开发人员管理。
请注意, 必须先设置此标志,(默认构造函数已经将其设置为true),然后才能调用QThreadPool:: start()。在QThreadPool:: start() 之后调用此函数将导致不可预测后果。

QRunnable 类是一个接口,用于表示需要执行的任务或代码段,由重新实现的 run() 函数表示。
一般使用 QThreadPool 在单独的线程中执行代码。要使用QRunnable创建线程,步骤如下:

  • 继承QRunnable。和QThread使用一样, 首先需要将你的线程类继承于QRunnable。
  • 重写run函数。还是和QThread一样,需要重写run函数,run是一个纯虚函数,必须重写。
  • 使用QThreadPool启动线程

与QThread的区别

  • 与外界通信方式不同。由于QThread是继承于QObject的,但QRunnable不是,所以在QThread线程中可以直接将线程中执行的结果通过信号的方式发到主程序,而QRunnable线程不能用信号槽,只能通过别的方式。
  • 启动线程方式不同。QThread线程可以直接调用start()函数启动,而QRunnable线程需要借助QThreadPool进行启动。
  • 资源管理不同。QThread线程对象需要手动去管理删除和释放,而QRunnable则会在QThreadPool调用完成后自动释放。

总结:

  • 作为Qt类中少有的基类, QRunnable提供了简洁有效的可运行对象的创建. 用QRunnable来创建独立的运行对象来运行 不涉及界面元素的数据处理过程 非常合适.
  • 优点: 创建过程简洁, 使用方便, 配合着自身的autoDelete特性, 有点“招之即来, 挥之即去”的感觉.
  • 缺点: 无法实时提供自身的运行状态.

QRunnable外界通信的方法:

  1. 使用多继承。让我们的自定义线程类同时继承于QRunnable和QObject,这样就可以使用信号和槽,但是多线程使用比较麻烦,特别是继承于自定义的类时,容易出现接口混乱,所以在项目中尽量少用多继承。
  2. 使用QMetaObject::invokeMethod。
#include <QRunnable>

class demo_runable : public QRunnable
{
public:
    void run() override;
};

#include "demo_runable.h"
#include <QDebug>
#include <QThread>

void demo_runable::run() {
    qInfo() << "demo runnable in " << QThread::currentThreadId();
    qDebug() << "开始执行线程";
    QThread::sleep(3);
    qDebug() << "线程结束";
}

在主线程中运行:

qInfo() << "++++++++++++++++++";
    run_worker = new demo_runable;
    QThreadPool::globalInstance()->start(run_worker);

Qt Concurrent

QtConcurrent模块提供了处理一些常见并行计算模式的高级函数:map、filter 和 reduce。与使用 QThread 和 QRunnable 不同,这些函数从不需要使用互斥锁或信号量等低级线程原语。相反,它们返回一个 QFuture 对象,可用于在函数准备好时检索函数的结果。QFuture 还可用于查询计算进度和暂停 / 恢复 / 取消计算。为了方便起见,QFutureWatcher 支持通过信号和插槽与 QFuture 进行交互。
QtConcurrent的map、filter 和 reduce会自动在所有可用的处理器核心上分配计算任务,因此现在编写的应用程序在以后部署到具有更多核心的系统上时仍能继续扩展。
这个模块还提供了QtConcurrent::run()() 函数,该函数可以在另一个线程中运行任何函数。然而,QtConcurrent::run() 仅支持map、filter 和 reduce可用功能的一个子集。QFuture可用于检索函数的返回值并检查线程是否正在运行。但是,对QtConcurrent::run() 的调用仅使用一个线程,不能暂停 / 恢复 / 取消,也不能查询进度。

QtConcurrent基本使用
  1. QtConcurrent的常用方法有QtConcurrent::run()、QtConcurrent::map()、QtConcurrent::filter()。map和filter还有其衍生的方法,例如还有mapped()、mappedReduced()、filtered()、filteredReduced。下面通过使用这几个方法来了解QtConcurrent的使用方法。
  2. run方法
    run方法是通过另开一个线程来执行用户指定的函数,需要注意的是这个线程是在线程池中获取的,也就是说这个线程是不需要手动释放的,运行完指定的函数线程会自动释放。下面通过几个示例说明一下怎么使用。
  • 调用用户自定义的函数
#include <QFuture>
#include <QtConcurrent>

QString hello(QString name,QString name1,QString name2,QString name3)
{
    qInfo() << "hello" <<name << "from" <<QThread::currentThread();

    for(int i=0; i<3; i++){
        QThread::sleep(1);
        qInfo() << QString("[%1] i = %2").arg(name).arg(i);
    }

    return name+name1+name2+name3;
}


    QFuture<QString> f1 = QtConcurrent::run(hello,QString("Alice"),QString("Alice"),QString("Alice"),QString("Alice"));
    QFuture<QString> f2 = QtConcurrent::run(hello,QString("Bob"),QString("Bob"),QString("Bob"),QString("Bob"));
    //QFuture::result()获取单个返回值
    qInfo() << "get result";
    qDebug() << f1.result();
    qDebug() << f2.result();
    //等待结束释放
    qInfo() << "wait finished";
    f1.waitForFinished();
    f2.waitForFinished();
  • 调用qt类库中的函数
    QByteArray bytearray = "h,ell,o wo,r,ld";
    //调用qt类中的函数
    QFuture<QList<QByteArray> > future = QtConcurrent::run(&QByteArray::split,bytearray,',');
    qDebug() << "result: " << future.result();

    future.waitForFinished();
  1. map函数及其衍生函数
  • map 函数用于需要更改原容器中数据的使用场景,对容器中的每个项目都调用一次函数,且每次调用都是单独的一个线程。这个没有返回新容器,所以不能通过future获取结果。线程也来自线程池,和run方法类似

QString toUperrMapped(const QString &str){
    return str.toUpper();
}

    QStringList strWords;
    strWords << "Apple" << "Banana" << "cow" << "dog" << "Egg";
    //第一个参数是原容器,第二参数是每个项目需要调用的方法
    auto future = QtConcurrent::map(strWords,toUpperMap);
    future.waitForFinished();
    qDebug() << "result: " << strWords;
  • mapped方法
    mapped 函数用于不改变原容器数据,返回处理新容器结果的使用场景,与map类似,因为返回新容器,所以能通过future获取结果
QString toUperrMappedReduced(const QString &str){
    return str.toUpper();
}

    QStringList oldStr;
    oldStr << "Apple" << "Banana" << "cow" << "dog" << "Egg";
    auto future = QtConcurrent::mapped(oldStr,toUperrMapped);
    future.waitForFinished();
    qDebug() << "result: " << future.results();//results()返回全部数据,这里如果调用result()只会返回一个数据
  • mappedReduced()方法
    mappedReduced 用于mapped处理后的结果还需要进行处理的使用场景。
QString toUperrMappedReduced(const QString &str){
    return str.toUpper();
}
//进一步处理的函数
void reduceFun(QList<QString> &dictionary,const QString &string){
    dictionary.push_back(QString("result:")+string);
}


    QStringList oldStrReduced;
    oldStrReduced << "Apple" << "Banana" << "cow" << "dog" << "Egg";
    //最后一个参数是为了保证调用的顺序与输出的顺序一致,否则输出的结果顺序是不确定的,因为每个项目都是单独的一个线程处理,所以输出结果不一样。
    auto future = QtConcurrent::mappedReduced(oldStrReduced,toUperrMappedReduced,reduceFun,QtConcurrent::OrderedReduce);
    future.waitForFinished();
    qDebug() << future.results();
  1. filter函数及其衍生函数
    filter函数与map函数类似,其衍生函数也与map的衍生函数类似,只是函数用于过滤。这里通过一个例子全部展示。
void reduceFun(QList<QString> &dictionary,const QString &string){
    dictionary.push_back(QString("result:")+string);
}

bool fiter (QString string){
    if(string.length()>3){ //只要长度大于3的字符串
        return true;
    }else return false;
}

    QStringList oldStrfilter;
    oldStrfilter << "Apple" << "Banana" << "cow" << "dog" << "Egg";
    auto future1 = QtConcurrent::filter(oldStrfilter,fiter);
    //filtered函数与mapped函数类似,只是函数用于过滤
    auto future2 = QtConcurrent::filtered(oldStrfilter,fiter);
    //filteredReduced函数与mappedReduced函数类似,只是函数用于过滤
    auto future3 = QtConcurrent::filteredReduced(oldStrfilter,fiter,reduceFun);
    future1.waitForFinished();
    future2.waitForFinished();
    future3.waitForFinished();

    qDebug() << oldStrfilter;
    qDebug() << future2.results();
    qDebug() << future3.results();

总结

  • QtConcurrent::run,在线程池内起一个线程来执行一个函数。
  • QtConcurrent::map, 用于并行处理一批数据的场景。
  • QtConcurrent::filter,一般用于对一批数据的过滤操作。

选择合适的方法

FeatureQThreadQRunnable and QThreadPoolQtConcurrent::run()Qt Concurrent (Map, Filter, Reduce)WorkerScript
LanguageC++C++C++C++QML
Thread priority can be specifiedYesYes
Thread can run an event loopYes
Thread can receive data updates through signalsYes (received by a worker QObject)Yes (received by WorkerScript)
Thread can be controlled using signalsYes (received by QThread)Yes (received by QFutureWatcher)
Thread can be monitored through a QFuturePartiallyYes
Built-in ability to pause/resume/cancelYes

使用场景:

生命周期开发任务解决方案
一次调用在另一个线程中运行一个函数,函数完成时退出线程1. 编写函数,使用QtConcurrent::run 运行它;2. 派生QRunnable,使用QThreadPool::globalInstance()->start() 运行它; 3. 派生QThread,重新实现QThread::run() ,使用QThread::start() 运行它
一次调用需要操作一个容器中所有的项。使用处理器所有可用的核心。一个常见的例子是从图像列表生成缩略图。QtConcurrent 提供了map()函你数来将操作应用到容器中的每一个元素,提供了fitler()函数来选择容器元素,以及指定reduce函数作为选项来组合剩余元素。
一次调用一个耗时运行的操作需要放入另一个线程。在处理过程中,状态信息需要发送会GUI线程。使用QThread,重新实现run函数并根据需要发送信号。使用信号槽的queued连接方式将信号连接到GUI线程的槽函数。
持久运行生存在另一个线程中的对象,根据要求需要执行不同的任务。这意味着工作线程需要双向的通讯。派生一个QObject对象并实现需要的信号和槽,将对象移动到一个运行有事件循环的线程中并通过queued方式连接的信号槽进行通讯。
持久运行生存在另一个线程中的对象,执行诸如轮询端口等重复的任务并与GUI线程通讯。同上,但是在工作线程中使用一个定时器来轮询。尽管如此,处理轮询的最好的解决方案是彻底避免它。有时QSocketNotifer是一个替代。
Lifetime of threadOperationSolution
One callRun a new linear function within another thread, optionally with progress updates during the run.- Place the function in a reimplementation of QThread::run() and start the QThread. Emit signals to update progress. OR
- Place the function in a reimplementation of QRunnable::run() and add the QRunnable to a QThreadPool. Write to a thread-safe variable to update progress. OR
- Run the function using QtConcurrent::run(). Write to a thread-safe variable to update progress.
One callRun an existing function within another thread and get its return value.Run the function using QtConcurrent::run(). Have a QFutureWatcher emit the finished() signal when the function has returned, and call QFutureWatcher::result() to get the function’s return value.
One callPerform an operation on all items of a container, using all available cores. For example, producing thumbnails from a list of images.Use Qt Concurrent’s QtConcurrent::filter() function to select container elements, and the QtConcurrent::map() function to apply an operation to each element. To fold the output into a single result, use QtConcurrent::filteredReduced() and QtConcurrent::mappedReduced() instead.
One call/PermanentPerfrom a long computation in a pure QML application, and update the GUI when the results are ready.Place the computation code in a .js script and attach it to a WorkerScript instance. Call WorkerScript.sendMessage() to start the computation in a new thread. Let the script call sendMessage() too, to pass the result back to the GUI thread. Handle the result in onMessage and update the GUI there.
PermanentHave an object living in another thread that can perform different tasks upon request and/or can receive new data to work with.Subclass a QObject to create a worker. Instantiate this worker object and a QThread. Move the worker to the new thread. Send commands or data to the worker object over queued signal-slot connections.
PermanentRepeatedly perform an expensive operation in another thread, where the thread does not need to receive any signals or events.Write the infinite loop directly within a reimplementation of QThread::run(). Start the thread without an event loop. Let the thread emit signals to send data back to the GUI thread.

参考

Multithreading Technologies in Qt
Threading Basics
Thread Support in Qt
Threads and QObjects

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/973001.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JUC并发—8.并发安全集合一

大纲 1.JDK 1.7的HashMap的死循环与数据丢失 2.ConcurrentHashMap的并发安全 3.ConcurrentHashMap的设计介绍 4.ConcurrentHashMap的put操作流程 5.ConcurrentHashMap的Node数组初始化 6.ConcurrentHashMap对Hash冲突的处理 7.ConcurrentHashMap的并发扩容机制 8.Concu…

Java中的常用类 --String

学习目标 掌握String常用方法掌握StringBuilder、StringBuffer了解正则 1.String ● String是JDK中提前定义好的类型 其所在的包是java.lang ,String翻译过来表示字符串类型&#xff0c;也就是说String类中已经提前定义好了很多方法都是用来处理字符串的&#xff0c;所以Str…

wps中的js开发

严格区分大小写 /*** learn_js Macro*/ function test() {Range(D7).Value2Selection.Value2; // Selection.formula "100" }function Workbook_SheetSelectionChange(Sh, Target) {if(Sh.Name Sheet1) {test();}}function test2() {// 把I4单元格及其周边有数的单…

QT事件循环

文章目录 主事件循环事件循环事件调度器事件处理投递事件发送事件 事件循环的嵌套线程的事件循环deleteLater与事件循环QEventLoop类QEventLoop应用等待一段时间同步操作模拟模态对话框 参考 本文主要对QT中的事件循环做简单介绍和使用 Qt作为一个跨平台的UI框架&#xff0c;其…

3-知识图谱-知识图谱的存储与查询

基于关系型数据库的知识图谱存储 基于原生图的知识图谱存储 关系型数据库的局限性 因为关系数据库&#xff0c;不善于处理“关系” 图数据库&#xff1a; Relations Are First-class citizens 在关系数据库中&#xff0c;关系是隐藏表达的。通过外键关联实体&#xff0c;表达…

【HarmonyOS Next】鸿蒙监听手机按键

【HarmonyOS Next】鸿蒙监听手机按键 一、前言 应用开发中我们会遇到监听用户实体按键&#xff0c;或者扩展按键的需求。亦或者是在某些场景下&#xff0c;禁止用户按下某些按键的业务需求。 这两种需求&#xff0c;鸿蒙都提供了对应的监听事件进行处理。 onKeyEvent 默认的…

SpringCloud-Eureka初步使用

什么是REST是一组用于规范资源在网络中转移的表现形式软件架构设计风格.简单来说就是客户端和服务器之间的一种交互形式 什么是RESTful,满足了REST风格的接口或者程序,RESTful API是其中的接口,spring中提供了RestTemplate这个类,他强制执行了REST的规范,包括使用HTTP协议的状…

SpringBoot+uniApp日历备忘录小程序系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码1.日历渲染代码&#xff1a;2.保存备忘录代码&#xff1a;3.删除备忘录代码&#xff1a; 一、项目演示 项目演示地址&#xff1a; 视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBootuniApp框架开…

推荐给 Easysearch 新用户的几个 Elasticsearch 可视化工具

Easysearch 作为国产化的 Elasticsearch&#xff08;ES&#xff09;替代方案&#xff0c;兼容 Elasticsearch 生态系统中的多种工具。本文将介绍几款适合 Easysearch 用户的可视化工具&#xff0c;帮助您更高效地管理和查询数据。 1. Elasticsearch Head 插件 在ES培训经常提…

PHP+Apache+MySQL安装(Windows)

一、安装教程 参考链接1 参考链接2 二、问题描述 PHP安装目录下找不到php8apache2_4.dll PHP安装包下载错误 Apache Service Monitor: request operation has failed! 定位问题&#xff1a; 查看【事件查看器】 解决问题 安装或更新与PHP版本相对应的Visual C Redistribu…

捷米特 JM - RTU - TCP 网关应用 F - net 协议转 Modbus TCP 实现电脑控制流量计

一、项目背景 在某工业生产园区的供水系统中&#xff0c;为了精确监测和控制各个生产环节的用水流量&#xff0c;需要对分布在不同区域的多个流量计进行集中管理。这些流量计原本采用 F - net 协议进行数据传输&#xff0c;但园区的监控系统基于 Modbus TCP 协议进行数据交互&…

【Mysql】我在广州学Mysql 系列—— 有关日志管理的示例

ℹ️大家好&#xff0c;我是练小杰&#xff0c;今天星期四了&#xff0c;明天周五&#xff0c;美好的周末又要到了&#xff01;&#xff01;&#x1f606; 本文是对MySQL日志管理内容进行练习&#xff0c;后续将添加更多相关知识噢&#xff0c;谢谢各位的支持&#x1f64f; 复习…

JUC并发—8.并发安全集合二

大纲 1.JDK 1.7的HashMap的死循环与数据丢失 2.ConcurrentHashMap的并发安全 3.ConcurrentHashMap的设计介绍 4.ConcurrentHashMap的put操作流程 5.ConcurrentHashMap的Node数组初始化 6.ConcurrentHashMap对Hash冲突的处理 7.ConcurrentHashMap的并发扩容机制 8.Concu…

金融时间序列【量化理论】

业界常用的技术分析指标都与价格本身有关&#xff0c;而时间序列分析由于对数据平稳性的要求常常是基于收益率这样更加偏稳定的数据&#xff08;收益率由于会涨停和跌停每天最多10%&#xff09; 平稳性&#xff1a; 强平稳性&#xff1a;随时间变化&#xff0c;各个统计特征都…

nvm安装、管理node多版本以及配置环境变量【保姆级教程】

引言 不同的项目运行时可能需要不同的node版本才可以运行&#xff0c;由于来回进行卸载不同版本的node比较麻烦&#xff1b;所以需要使用node工程多版本管理。 本人在配置时&#xff0c;通过网络搜索教程&#xff0c;由于文章时间过老&#xff0c;或者文章的互相拷贝导致配置时…

8 SpringBootWeb案例(上): 查询【分页功能(分页插件)】、删除、新增、修改

文章目录 前言:SpringBootWeb案例1. 准备工作1.1 需求&环境搭建1.1.1 需求说明1.1.2 环境搭建1.2 开发规范1.2.1 开发规范-REST(不强求非要这种风格,传统风格有时候更方便)1.2.2 开发规范-统一响应结果和异常处理1.2.3 开发流程2. 部门管理2.1 查询部门2.1.1 原型和需求…

新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)

视频教程和更多福利在我主页简介或专栏里 &#xff08;不懂都可以来问我 专栏找我哦&#xff09; 如果对你有帮助你可以来专栏找我&#xff0c;我可以无偿分享给你对你更有帮助的一些经验和资料哦 目录&#xff1a; 一、XSS的三种类型&#xff1a; 二、XSS攻击的危害&#x…

用《软件方法》引导AI全流程高效开发

和“敏捷”的拍脑袋“试错”不同&#xff0c;《软件方法》一直强调严谨地思考、推导和建模。 如何尽量借助现有AI的力量&#xff0c;降低建模人员A→B→C→D的推导工作量&#xff0c;是一个非常有价值的课题。我们将用一个实例来分享和展示《软件方法》作者潘加宇的建议实践。…

全面收集中间件Exporter适配:从Redis到ActiveMQ,掌握监控数据采集的最佳实践

#作者&#xff1a;任少近 文章目录 说明&#xff1a;一 Redis的适配exporter版1.1 Redis的exporter源码版本1.2 Redis的exporter的releases版1.3 Redis_exporter版本选择理由1.4 Redis_exporter docer镜像 二 Zookeeper的适配exporter版2.1 Zookeeper的exporter源码版本2.2 Zo…

npm在install时提示要安装python问题处理

使用npm\yarn\pnpm下载以来的时候&#xff0c;一直提示python异常&#xff0c;有的项目安装了python之后&#xff0c;下载依赖还是异常 而且旧版本项目使用python2,新的使用Python3…很烦 解决方案1&#xff1a;cnpm 使用cnpm 安装教程&#xff1a; npm安装cnpm&#xff0c;解…