Qt QtConcurrent之 Run 函数用法

概述


Concurrent是并发的意思,QtConcurrent是一个命名空间,提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。这意味着今后编写的应用程序将在未来部署在多核系统上时继续扩展。

而这里要讲的是QtConcurrent::run函数的用法。
函数原型如下:

QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)


简单的说,QtConcurrent::run()函数会在一个单独的线程中执行,并且该线程取自全局QThreadPool,该函数的返回值通过QFuture API提供。
请注意:该函数可能不会立即运行; 函数只有在线程可用时才会运行。
通过QtConcurrent::run()返回的QFuture不支持取消、暂停,返回的QFuture只能用于查询函数的运行/完成状态和返回值。

导入模块
在 C++ API changes 有关于 Qt Concurrent 的更改说明

Qt Concurrent has been moved from Qt Core to its own module


大致意思就是,Qt Concurrent已经从 QtCore 中移除并成为了一个独立的模块。
所以在使用的时候需要在工程文件中导入模块,如下

QT += concurrent
示例
通过一个简单的例子来看一下效果

#include "widget.h"
#include <QDebug>
#include <QThread>
#include <QtConcurrent>
#include <QFuture>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_pBtn = new QPushButton("Click me",this);
    m_pBtn->setGeometry(100,100,220,70);
    connect(m_pBtn,&QPushButton::clicked,this,&Widget::onBtnClicked);
}

void func(QString str)
{
    qDebug() << __FUNCTION__ << str << QThread::currentThreadId() << QThread::currentThread();
}

void Widget::onBtnClicked()
{
#if 0
    QFuture<void> f1 =QtConcurrent::run(func,QString("aa"));
    f1.waitForFinished();
#else
    QFuture < void > future =  QtConcurrent::run([=](){
        qDebug() << __FUNCTION__  << QThread::currentThreadId() << QThread::currentThread();
    });
    QFuture < void > future2 = QtConcurrent::run([=](){
        qDebug() << __FUNCTION__  << QThread::currentThreadId() << QThread::currentThread();
    });
#endif
}


做了一个简单的界面,通过点击按钮来调用 QtConCurrent::run函数。

这里在按钮的槽函数中用了两种方法来调用,一种是调用外部 extern 函数,一种是使用了Lambda函数,两种方式只是写法上的不同,结果都是一样的。

注意:第一种方法调用外部函数,如果该函数是类的成员函数,会报以下错误 : reference to no-static member function must be called…
如下图:


定义外部函数

extern void func(QString str);


这里运行了第二种方法,Lambda函数来实现。
点击按钮后输出以下结果:

operator() 0x700005df0000 QThread(0x7fe63ccf8420, name = "Thread (pooled)")
operator() 0x700005e73000 QThread(0x7fe63cc56630, name = "Thread (pooled)")


连续调用两次,分别在不同的线程中执行的,这也就验证了该函数的作用。

问题来了
看到这儿,一定会有个疑问,上面说调用的函数必须是外部函数,通过 extern 来定义函数,那么,如果要调用类的成员函数怎么办呢?先来看看 Qt 文档的介绍。

使用成员函数
QtConcurrent :: run()也接受指向成员函数的指针。第一个参数必须是一个const引用或一个指向该类实例的指针。const成员函数一般传递 常量引用 (const reference),而非常量成员函数一般传递 指针 (pointer)

例如,在一个单独的线程中调用QByteArray :: split()(一个const成员函数)就像这样完成:

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


调用非const成员函数是这样完成的:

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


OK,大概清楚了,我们修改一下上面的示例,让 QtConCurrent::run去调用成员函数。
看代码:

#include "widget.h"
#include <QDebug>
#include <QThread>
#include <QtConcurrent>
#include <QFuture>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_pBtn = new QPushButton("Click me",this);
    m_pBtn->setGeometry(100,100,220,70);
    connect(m_pBtn,&QPushButton::clicked,this,&Widget::onBtnClicked);
}

void Widget::onBtnClicked()
{
#if 1
    //调用外部函数
    QFuture<void> f1 =QtConcurrent::run(func,QString(index++));
    //调用类成员函数
    QFuture<void> f2 =QtConcurrent::run(this,&Widget::myFunc,QString("aaa"));
#else  //使用Lambda函数实现
    QFuture < void > future =  QtConcurrent::run([=](){
        qDebug() << __FUNCTION__  << QThread::currentThreadId() << QThread::currentThread();
    });
    QFuture < void > future2 = QtConcurrent::run([=](){
        qDebug() << __FUNCTION__  << QThread::currentThreadId() << QThread::currentThread();
    });
#endif
}

void Widget::myFunc(const QString & str)
{
    qDebug() << __FUNCTION__ << str << QThread::currentThreadId() << QThread::currentThread();
}

void func(QString str)
{
    qDebug() << __FUNCTION__ << str << QThread::currentThreadId() << QThread::currentThread();
}


简单说明一下,这里新增了一个成员函数myFunc,在槽函数onBtnClicked中进行调用。

//调用类成员函数

    QFuture<void> f2 =QtConcurrent::run(this,&Widget::myFunc,QString("aaa"));


使用Lambda函数
调用lambda函数是这样完成的:

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


上面示例中已经添加了Lambda 函数的调用方法,如果说将要调用的函数执行的内容不多的话 可以考虑使用Lambda 函数,使用会比较简单。

上一篇:5G NSA切换信令流程


下一篇:https://www.johndcook.com/blog/2018/08/21/a-tale-of-two-elliptic-curves/