Qt 多线程之QtConcurrent::run() (**)

目录

Qt 多线程QtConcurrent::run使用进度条

QtConcurrent

QT多线程之QtConcurrent::run()

QFuture允许线程对一个或多个结果进行同步:做线程同步。

Qt线程之QtConCurrent

这样用Qt的concurrent,有什么缺点?

官方手册:QtConcurrent

如何正确使用QtConcurrent运行类成员函数

Qt多线程的三种方法QtConcurrent::run()+QThreadPool

Qt多线程管理之命名空间:QtConcurrent (***)

便捷的使用多线程并发类QtConcurrent,解决Qt在槽函数中执行耗时操作导致界面卡住的问题

注:?槽函数,相当于 MCU的中断函数?不应该用来执行耗时操作??

------------------------------------------------------------------

专门的工具,有专门的用途:没有包治百病的药,也没有无所不能的工具。

concurrent:Qt的concurrent::run()用法简单、便捷。但是有个致命的缺点,那就是难以管理,当你Run() 以后,你想主动停下来。

QSettings:不能用于写入 linux的 desktop文件。会导致空格,及其他一些特殊符号的改变。

QT QtConcurrent::run线程如何关闭

https://zhidao.baidu.com/question/452262251.html

run() 方法之后就自动关闭!

疑问:和 QSettings一样,关闭有 Qt系统控制,不是由程序员控制。

======================================

如何正确使用QtConcurrent运行类成员函数

   https://zhidao.baidu.com/question/2077377517539852828.html

使用QtConcurrent的代码如下:

void MainDialog::on_pushButton_toGrayImage_QConcurrent_clicked()
{
    QFuture future = QtConcurrent::run(this,&MainDialog::processImageByQtConcurrent);

   
    //imageAfterProceess 这个指针变量之前被无视,现在终于找到存在感了
    this->imageAfterProceess = future.result(); //接收处理结果
    this->showImageInNewWindow(this->imageAfterProceess);//新窗口显示处理后的图像
}

Qt 多线程QtConcurrent::run使用进度条

	QProgressDialog process(this);
	process.setWindowTitle(u8"测试");
	process.setLabelText(QString(u8"读取%1张影像...").arg(imagePaths.size()));
	process.setRange(0, imagePaths.size());
	process.setValue(0);
	process.setModal(true);
	process.setCancelButton(nullptr);//隐藏取消按钮
	process.show();

	//进程监视
	QFutureWatcher *watcher = new QFutureWatcher;
	QEventLoop loop;
	connect(watcher, &QFutureWatcher::finished, &loop, &QEventLoop::quit);
	connect(watcher, &QFutureWatcher::progressValueChanged, &process, &QProgressDialog::setValue);

	QFuture future = QtConcurrent::run([&]()
	{
		qDebug() << "线程开始" << QThread::currentThread();
		for (int i = 0; i < imagePaths.size(); ++i)
		{
			emit watcher->progressValueChanged(i + 1);
		}
		qDebug() << "线程结束" << QThread::currentThread();
	});
	watcher->setFuture(future);
	loop.exec();

    QProgressDialog process(this);
    process.setWindowTitle(u8"测试");
    process.setLabelText(QString(u8"读取%1张影像...").arg(imagePaths.size()));
    process.setRange(0, imagePaths.size());
    process.setValue(0);
    process.setModal(true);
    process.setCancelButton(nullptr);//隐藏取消按钮
    process.show();

                 

    //进程监视
    QFutureWatcher *watcher = new QFutureWatcher;

   
    QEventLoop loop;
    connect(watcher, &QFutureWatcher::finished, &loop, &QEventLoop::quit);
    connect(watcher, &QFutureWatcher::progressValueChanged, &process, &QProgressDialog::setValue);

   

    QFuture future = QtConcurrent::run([&]()
    {
        qDebug() << "线程开始" << QThread::currentThread();
        for (int i = 0; i < imagePaths.size(); ++i)
        {
            emit watcher->progressValueChanged(i + 1);
        }
        qDebug() << "线程结束" << QThread::currentThread();
    });
    watcher->setFuture(future);
    loop.exec();

Qt 多线程QtConcurrent::run使用进度条_qtconcurrent 进度条_乔巴的树洞的博客-CSDN博客

这样用Qt的concurrent,有什么缺点?

  https://www.zhihu.com/question/457680890/answer/2911898178

-----------------------------

手册里面说的很明白了,继承QThread来运行代码的话没什么问题,也可以用signal接受控制信号,但缺点是并不能在QThread中定义slot。可是如果用 moveToThread 的话就没得这个问题了。

补充一下,

QThread 对象在构造时还是在当前的 thread 里面执行的,所以它的本体也在这里,当然也包括子类中定义的各种 slot。所以当调用它的 slot 时,还是在当前 thread 里面执行的,并不是在我们想要用 QThread 管理的那个 thread 里面。

-----------------------------

Qt的concurrent::run()用法简单、便捷。但是有个致命的缺点,那就是难以管理,当你Run() 以后,你想主动停下来,不好意思,这个很难搞。有点请神容易、送神难。

我们来看看你的Run()代码。

 QtConcurrent::run([this]()
        {
            emit workingStart(QThread::currentThreadId());
            for(int i=0;i<1024*1024*1024;++i)
            {
                //some work.
            }
            emit workingFinished(QThread::currentThreadId());
        }

这个代码中,执行了线程,如果你的some work中有使用资源,且主线程也在使用。在循环执行函数中没有停止条件,要停止,线程停止不下来,就只能等执行完,会让主线程一直处于阻塞状态。

用多了就会发现这个问提,不过貌似Qt6.0做个优化,不知道是否有停止它的更优雅的方式。

官方手册:QtConcurrent

Qt 5.14.2 Reference Documentation

QtConcurrent Namespace
The QtConcurrent namespace provides high-level APIs that make it possible to write multi-threaded programs without using low-level threading primitives. More...

Header:
#include
qmake:
QT += concurrent
Since:
Qt 4.4

This namespace was introduced in Qt 4.4.
Obsolete members

Concurrent Run

The QtConcurrent::run() function runs a function in a separate thread. The return value of the function is made available through the QFuture API.
This function is a part of the Qt Concurrent framework.

Running a Function in a Separate Thread

To run a function in another thread, use QtConcurrent::run():

  extern void aFunction();
  QFuture future = QtConcurrent::run(aFunction);

This will run aFunction in a separate thread obtained from the default QThreadPool. You can use the QFuture and QFutureWatcher classes to monitor the status of the function.
To use a dedicated thread pool, you can pass the QThreadPool as the first argument:

  extern void aFunction();
  QThreadPool pool;
  QFuture future = QtConcurrent::run(&pool, aFunction);

Passing Arguments to the Function

Passing arguments to the function is done by adding them to the QtConcurrent::run() call immediately after the function name. For example:

  extern void aFunctionWithArguments(int arg1, double arg2, const QString &string);

  int integer = ...;
  double floatingPoint = ...;
  QString string = ...;

  

  QFuture future = QtConcurrent::run(aFunctionWithArguments, integer, floatingPoint, string);

A copy of each argument is made at the point where QtConcurrent::run() is called, and these values are passed to the thread when it begins executing the function. Changes made to the arguments after calling QtConcurrent::run() are not visible to the thread.

Returning Values from the Function

Any return value from the function is available via QFuture:

  extern QString functionReturningAString();
  QFuture future = QtConcurrent::run(functionReturningAString);
  ...
  QString result = future.result();

As documented above, passing arguments is done like this:

  extern QString someFunction(const QByteArray &input);

  QByteArray bytearray = ...;

  QFuture future = QtConcurrent::run(someFunction, bytearray);
  ...
  QString result = future.result();

注意:run()中,函数名称 和 函数的参数,是分开来写的。

Note that the QFuture::result() function blocks and waits for the result to become available. Use QFutureWatcher to get notification when the function has finished execution and the result is available.

Additional API Features

Using Member Functions

QtConcurrent::run() also accepts pointers to member functions. The first argument must be either a const reference or a pointer to an instance of the class. Passing by const reference is useful when calling const member functions; passing by pointer is useful for calling non-const member functions that modify the instance.
For example, calling QByteArray::split() (a const member function) in a separate thread is done like this:

  // call 'QList  QByteArray::split(char sep) const' in a separate thread
  QByteArray bytearray = "hello world";
  QFuture > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
  ...
  QList result = future.result();

Calling a non-const member function is done like this:

  // call 'void QImage::invertPixels(InvertMode mode)' in a separate thread
  QImage image = ...;
  QFuture future = QtConcurrent::run(&image, &QImage::invertPixels, QImage::InvertRgba);
  ...
  future.waitForFinished();
  // At this point, the pixels in 'image' have been inverted

注意 1 :语法

bool test_zzz(QString a, int b); // in class AbClass
    
//    AbClass * xxx =new AbClass;
//    QFuture future = QtConcurrent::run(xxx, &AbClass::test_zzz, a, b);

    //=============================================

    AbClass xxx;
    QFuture future = QtConcurrent::run(&xxx, &AbClass::test_zzz, a, b);

疑问 1:在函数内部运行 QtConcurrent::run(),有些函数实现 NG,需要在函数外部定义 AbClass xxx;

疑问 2:函数的功能正常完成。但最后 会输出 N行的下面这类错误信息,然后,ok结束。

QObject::startTimer: Timers cannot be started from another thread
QObject::killTimer: Timers cannot be stopped from another thread
QObject::startTimer: Timers cannot be started from another thread
QObject::killTimer: Timers cannot be stopped from another thread

疑问 3:在 win /linux切换时,QtConcurrent 线程似乎有问题 ?

Using Lambda Functions

Calling a lambda function is done like this:

  QFuture future = QtConcurrent::run([=]() {
      // Code in this block will run in another thread
  });
  ...

==================================

Qt线程之QtConCurrent

https://www.freesion.com/article/55361142060/

在使用Qt创建线程的时候突发奇想,竟然想把UI显示放到子线程中去,然后让主线程去处理业务逻辑,说干就干,于是qt就报出了以下错误来告诉我这样做不可以:

ASSERT failure in QWidget: "Widgets must be created in the GUI thread.", file kernel\qwidget.cpp, line 1145

目前我在项目中使用线程的场景:

1.线程生命周期和主进程生命周期相同,协同主进程去处理一些业务。

2.线程生命周期只在处理某个业务时用,其它时候不调用。

关于第一种创建线程的方式就不多说了,可以继承QThread,如果是在linux下也可以直接使用 pthread_create等。这里只简单说下qt的高级线程接口:QtConCurrent。

在QT5中,该API从core中移除,如果要引用该API需要在xxxx.pro文件中加入:

QT += concurrent

提到QtConcurrent就不得不说QFuture,关于QFuture官方文档是这样说的:

QFuture allows threads to be synchronized against one or more results which will be ready at a later point in time. The result can be of any type that has a default constructor and a copy constructor. If a result is not available at the time of calling the result(), resultAt(), or results() functions, QFuture will wait until the result becomes available. You can use the isResultReadyAt() function to determine if a result is ready or not. For QFuture objects that report more than one result, the resultCount() function returns the number of continuous results. This means that it is always safe to iterate through the results from 0 to resultCount().

大概就是:“QFuture允许线程对一个或多个结果进行同步”,了解到这里就差不多了,我们就使用它来做线程同步。

此处我使用它主要是后台要处理业务,而UI需要进行提示,当业务处理完毕时,要结束提示,返回主界面。如下图:

QtConcurrent

QT += concurrent
//无法触发弹窗控件等,可通过信号的形式触发无法使用的功能
 QtConcurrent::run([=](){
        qDebug() << __FUNCTION__  << QThread::currentThreadId() << QThread::currentThread();
    });
QThreadPool pool;
QtConcurrent::run(&pool, func);

数据传递

当匿名函数[=]时可捕捉到外部变量,我尝试了传控件不行。
内部变量传出来

int id=5;
QtConcurrent::run([=](){
            foreach (auto var, m_mapFileData)
            {
                if(var->getId()==id)
                {
                    QString filePath;
                    if(!var->getPath().contains(":"))
                    {
                        filePath.append(QDir::currentPath()).append("/");
                    }
                    filePath.append(var->getPath());
                    QFile file(filePath);
                    if(file.open(QIODevice::ReadOnly | QIODevice::Text))
                    {
                        QTextStream in(&file);
                        QVector vect;
                        qRegisterMetaType< QVector>("QVector&");//注册此变量到线程
                        while(!in.atEnd())
                        {
                            QString strLine=in.readLine();
                            vect.append(strLine);
                        }
                        file.close();
                        emit signal_fillPerview(vect);//通过信号将数据传出
                    }
                }
            }
           });



作者:c之气三段
链接:https://www.jianshu.com/p/eac095ee295b

===================================

QT多线程之QtConcurrent::run()

QT有几种可以实现多线程编程的方式,其中最方便使用,最便携的一定是QtConcurrent::run()了,这是一个模板函数,有很多的重载原型。

//在新的线程中调用普通函数

template QFuture QtConcurrent::run(Function function, ...)

//使用线程池中的线程调用普通函数

template QFuture QtConcurrent::run(QThreadPool *pool, Function function, ...)

//在新的线程里调用成员函数 有多个重载实现不同的参数个数

template QFuture QtConcurrent::run(className *obejct, Function function, ...)

这些模板都有多个重载,支持带参数函数,切可以带多个参数。

QFuture

QFuture 也是一个模板类,它可以监控线程的运行状态有,并可以获取在另一个线程中运行的函数的返回值,返回值通过调用result()函数获取。

需要注意的是获取返回值之前,最好先调用waitForFinished()保证线程执行完毕。

示例

.h文件

#pragma once

#include

#include

#include

class ThreadTest {
    
public:
    
    ThreadTest();
    
    ~ThreadTest() = default;
    
public:
    
    void anotherThread();
    
    std::string anotherThreadReturn();
    
};

.cpp   :  run(this, &ThreadTest::anotherThread);

#include "QtThreadTest.h"

#include

#include

void normalFunction() {
    
    std::cerr << "Normal function thread ID: " << QThread::currentThreadId() << std::endl;
    
}

ThreadTest::ThreadTest()

{
    
    std::cerr << "main thread ID: " << QThread::currentThreadId() << std::endl;
    
    QtConcurrent::run(normalFunction);
    
    QFuture future = QtConcurrent::run(this, &ThreadTest::anotherThread);
    
    QFuture future1 = QtConcurrent::run(this, &ThreadTest::anotherThreadReturn);
    
    future.waitForFinished();
    
    future1.waitForFinished();
    
    std::cerr << "anotherThreadReturn result: " << future1.result() << std::endl;
    
}

void ThreadTest::anotherThread()

{
    
    std::cerr << "Another thread ID: " << QThread::currentThreadId() << std::endl;
    
}

std::string ThreadTest::anotherThreadReturn()

{
    
    std::cerr << "Another return thread ID: " << QThread::currentThreadId() << std::endl;
    
    return "This is result";
    
}

新建对象后的输出结果:

main thread ID: 000000000000499C
Normal function thread ID: 0000000000007830
Another thread ID: 0000000000006BA4
Another return thread ID: 0000000000000A2C
anotherThreadReturn result: This is result

总结
  1. 调用run() 之后,函数不一定会被立即执行,如果有多个run()被调用,函数的调用顺序不一定是run()的调用顺序,这些都和线程的调度有关系。
  2. run(function) 实际上等价于run(QThreadPool::globalInstance(),function)

如果只是简单的想在其他线程中调用某个函数,不需要复杂的数据同步,那么QtConcurrent::run() 相比其他实现多线程的方式绝对是不二之选。

2022-11-1 补充

之前因为看漏了文档,导致今天被自己坑了一把。

Qt 多线程之QtConcurrent::run() (**)_第1张图片

文档中提到,Run中的函数参数,都是拷贝之后使用副本如果在线程中调用的函数参数类型是引用类型,那么是不能正常对原有的变量进行改变的。

比如下面的例子

void refrencesPar(int& a, int& b)

{
    
    a = 1;
    b = 1;
    
    std::cerr << "call refrencesPar() " << "a : " << a << ",b: " << b <<         
                 " |a address:" << &a << " |b address: " << &b < }

ThreadTest::ThreadTest()

{
    int a = 0, b = 0;
    
    auto future = QtConcurrent::run(refrencesPar, a, b);
    
    future.waitForFinished();
    
    std::cerr << "a : " << a << ",b: " << b <<            
                 " |a address:" << &a << " |b address: " << &b << std::endl;
}

运行代码输出的结果:

image-20221101202946904

可以看到,两次输出的a,b变量的地址是不一样的,因为run在调用函数之前已经拷贝了一份副本

所以,如果想在函数中改变外面变量的值,这里只能使用指针

Qt多线程的三种方法QtConcurrent::run()+QThreadPool

一、简介

QtConcurrent这是一个高级 API,构建于QThreadPool之上,它提供更高层次的函数接口(APIs),使所写的程序,可根据计算机的CPU核数,自动调整运行的线程数量。通常用于处理大多数通用的并行计算模式。

注意,QtConcurrent 是一个命名空间不是一个类,因此,其中的所有函数都是命名空间内的全局函数。

二、使用方法

1. 添加concurrent

从Qt4.4之后,QtConcurrent从core模块中独立出来,作为单独的模块。

所以先要在.pro中添加 QT += concurrent

QT += core gui concurrent

2. 使用方法

可以分别将外部函数,lambda表达式,成员函数运行在某一个线程中。

调用外部函数

调用Lambda表达式(同外部函数)

调用成员函数

调用常量成员函数

代码如下:

//1.调用外部函数func1,并得到返回值
QFuture f1 =QtConcurrent::run(func1,QString("aa"));
f1.waitForFinished();
qDebug()< future = QtConcurrent::run([=](){
qDebug() << __FUNCTION__ << QThread::currentThreadId() << QThread::currentThread();
});
QFuture < void > future2 = QtConcurrent::run([=](){
qDebug() << __FUNCTION__ << QThread::currentThreadId() << QThread::currentThread();
});
  
//3.调用成员函数
QFuture future3 = QtConcurrent::run(this, &Dialog::memberFun1);
  
//4.常量成员函数QByteArray::split()
QByteArray bytearray = "hello,world";
QFuture> future4 = QtConcurrent::run(bytearray, &QByteArray::split, ',');
QList result = future4.result();
qDebug()<<"result:"<

运行如下:

func1 "aa" 0xacc QThreadPoolThread(0x3d421d0, name = "Thread (pooled)")
"func1 return"
operator() 0xacc QThreadPoolThread(0x3d421d0, name = "Thread (pooled)")
operator() 0x70ac QThreadPoolThread(0x3d42090, name = "Thread (pooled)")
memberFun1 0x8a88 QThreadPoolThread(0x3d421b0, name = "Thread (pooled)")
result: ("hello", "world")

三、Qt多线程三种方法总结

Qt 多线程之QtConcurrent::run() (**)_第2张图片

四、不同开发场景下的考虑

需要线程的生命周期 开发场景 解决方案
单次调用 在其他的线程中运行一个方法,当方法运行结束后退出线程。

(1)编写一个函数,然后利用 QtConcurrent::run()运行它;

(2)从QRunnable 派生一个类,并利用全局线程池QThreadPool::globalInstance()->start()来运行它。

(3) 从QThread派生一个类, 重载QThread::run() 方法并使用QThread::start()来运行它。

单次调用 一个耗时的操作必须放到另一个线程中运行。在这期间,状态信息必须发送到GUI线程中。 使用 QThread,,重载run方法并根据情况发送信号。.使用queued信号/槽连接来连接信号与GUI线程的槽。
常驻 有一对象位于另一个线程中,将让其根据不同的请求执行不同的操作。 这意味与工作者线程之间的通信是必须的。 从QObject 派生一个类并实现必要的槽和信号,将对象移到一个具有事件循环的线程中,并通过queued信号/槽连接与对象进行通信。

Qt开发学习资料:

Qt多线程管理之命名空间:QtConcurrent (***)

      在.pro中添加 QT += concurrent(Qt 4.4之后)

        QtConcurrent为Qt线程管理提供了一套线程管理的高级API,被调用的线程数量以及调用的时间由硬件核数以及操作系统的调度策略所起作用。

QtConcurrent 未提供信号槽机制,可以使用 QFutureWatcher进行线程通信

       启动线程的方法:QFuture   QtConcurrent::run(Function function, ...)

        该run方法通过Function function对象来传递可调用对象,如:

                函数对象

                函数指针

                lambda表达式

                ...等

        可调用函数的参数由run参数列表的省略号传递。

        通过Function function,可以将可调用对象转换为QRunnable对象,并将该对象提交到全局QThreadPool中。在QThreadPool对象中,处理线程对象被预先创建,这些线程对象会轮流处理传递给它们的任务。(QThreadPool对象可以不用创建)

        所调用对象的返回值(若所调用对象有返回值的情况下)由run函数的返回值QFuture给出,其中T为返回值所对应的类型 。可以通过T QFuture::result() const得到。

    #include
    #include
    #include
     
    int function(int arg1, int arg2)
    {
        // 传递给run()的函数,将在新线程中运行

    }
     
    int main(int argc, char *argv[])
    {
        QThreadPool::globalInstance()->setMaxThreadCount(3); // 设置线程池的最大线程数
     
        QFuture f_result = QtConcurrent::run(&function, 10, 20); //开启新线程执行任务,并传递参数,当run被启动时会返回f_result,任务是异步执行的
        f_result.waitForFinished(); //阻塞当前线程,直到任务执行完成或被取消
        qDebug()<      
        return 0;
    }
     
     
    int main(int argc, char *argv[])
    {
        QThreadPool::globalInstance()->setMaxThreadCount(3); // 设置线程池的最大线程数
     
        QFuture f_result = QtConcurrent::run(&function, 10, 20); //开启新线程执行任务,并传递参数,当run被启动时会返回f_result,任务是异步执行的
        
        //进行消息传递
        QFutureWatcher watcher;
      QObject::connect(&watcher, &QFutureWatcher::finished, [](){
        // 任务完成之后的处理代码
        // ...
      });
      
      // 监视异步任务的执行进度和结果
      watcher.setFuture(f_result);
     
      // ...
      
        return 0;
    }

————————————————
版权声明:本文为CSDN博主「Demons ?」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fffjvjxrx/article/details/131233239

便捷的使用多线程并发类QtConcurrent,解决Qt在槽函数中执行耗时操作导致界面卡住的问题

目录

一、QtConcurrent的使用

1、QtConcurrent介绍

1.1QtConcurrent::run() 原型

2、QtConcurrent::run()的使用

3、QtConcurrent::run()的参数使用

3.1将自己的函数声明为静态函数

 3.2 在静态函数用静态指针访问UI和成员函数


一、QtConcurrent的使用

1、QtConcurrent介绍

QtConcurrent::run() 函数在单独的线程中运行一个函数,也就是说这是一种便捷的使用多线程的方法,实现异步运算。

1.1QtConcurrent::run() 原型

QtConcurrent::run()的原型很多,但是最基本的是以下 4 种,返回类型是QFuture,返回的结果在QFuture里面。(T是函数的返回值类型  如 void int )

QFuture< T > run( Function function ),无参
QFuture< T > run( Function function, const Arg1 &arg1 ⋯ \cdots⋯ const Arg5 &arg5),有参,参数最多5个
QFuture< T > run( const Class *object, Function function ⋯ \cdots⋯) 成员函数
QFuture< T > run(QThreadPool *pool, const Class *object, Function function ⋯ \cdots⋯) 指定 QThreadPool 的成员函数

2、QtConcurrent::run()的使用

2.1 在pro文件中添加concurrent模块(如果VS+QT,就在项目属性里添加concurrent模块)

QT       += core gui concurrent
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11

2.2包含头文件 

#include 

2.3在响应的槽函数中调用

QFuture future = QtConcurrent::run(this, &MainWindow::自己的函数);
	while (!future.isFinished())
	{
		QApplication::processEvents(QEventLoop::AllEvents, 30);
	}

注意:有可能在这块你出现了问题,别着急,这是因为“自己的函数”需要是静态的才可以,如何把自己的函数写成静态的呢,我们接着操作。

3、QtConcurrent::run()的参数使用

3.1将自己的函数声明为静态函数

。。。。。。省略。。。。。。。

你可能感兴趣的:(Qt,qt)