QT多线程之QtConcurrent::run()

2022年4月1日 6847点热度 15人点赞 0条评论

QT多线程之QtConcurrent::run()

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

//在新的线程中调用普通函数 
template <typename T> QFuture<T> QtConcurrent::run(Function function, ...) 
//使用线程池中的线程调用普通函数 
template <typename T> QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...) 
//在新的线程里调用成员函数 有多个重载实现不同的参数个数 
template <typename T> QFuture<T> QtConcurrent::run(className *obejct, Function function, ...)

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

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

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

示例

.h文件

#pragma once
#include <QtCore/QObject>
#include <QtConcurrent/QtConcurrent>
#include <QtCore/QThread>
class ThreadTest {
public:
    ThreadTest();
    ~ThreadTest() = default;

public:
    void anotherThread();
    std::string anotherThreadReturn();
};

.cpp

#include "QtThreadTest.h"
#include <iostream>
#include <QFuture>

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<void> future = QtConcurrent::run(this, &ThreadTest::anotherThread);
    QFuture<std::string> 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 补充

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

image-20221101202710317

文档中提到,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 <<std::endl;
}

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在调用函数之前已经拷贝了一份副本,所以如果想在函数中改变外面变量的值,这里只能使用指针。

大脸猫

这个人虽然很勤快,但什么也没有留下!

文章评论