QtConcurrent中的run()函数的使用

有两种模式: Basic模式和Promise模式.

好像Promise模式是Qt6里面新增的, 以前没有

Basic模式的使用:

使用1: 使用一个外部全局函数

1
2
3
4
5
6
7
8
9
// 最简单的场景: func1()是一个无参数的函数
extern void func1();
QFuture<void> future = QtConcurrent::run(func1);

// 稍微复杂一点, 带参数场景:
extern void func2(int arg1, double arg2);
int arg1 = ...;
double arg2 = ...;
QFuture<void> future = QtConcurrent::run(func2, arg1, arg2);

场景2: 重载的全局函数

1
2
3
4
5
6
7
8
9
10
11
12
13
// foo有两个重载, 要使用foo/1:
void foo(int arg1);
void foo(int arg1, double arg2);

// 方法1: 使用Lambda,
QFuture<void> future1 = QtConcurrent::run([](){foo(42);});

// 方法2:
QFuture<void> future2 = QtConcurrent::run(static_cast<void(*)(int)>(foo), 42);

// 方法3:
QFuture<void> future3 = QtConcurrent::run(qOverload<int>(foo), 42);

场景3: 获取返回值

1
2
3
4
5
QString foo();
QFuture<QString> future = QtConcurrent::run(foo);
...
// 获取返回值. future.result()会阻塞等待.
QString result = future.result();

场景4: 类成员函数

如果调用的是const成员函数, 因为不需要修改类实例, 可以以传值的方法进行.
如果调用的是非const成员函数, 则必须将类实例以指针的方法传进去.
注意参数的顺序: 先是类方法签名, 然后是类实例(或指针), 然后是依次的参数:

这个有点奇怪, 我印象中, 在Qt5中, 对象是第一个参数?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// 情况1: 调用类的const方法.
// 调用QByteArray::split(char sep) const 方法. 这是QByteArray的一个const方法:
// 这个时候, 可以直接传值给run:
QByteArray bytearray = "hello world";
// 这里直接将bytearray传进去
QFuture<QList<QByteArray> > future = QtConcurrent::run(&QByteArray::split, bytearray, ' ');
...
QList<QByteArray> result = future.result();

// 情况2: 调用类的非const方法
// 这个时候, 需要将类示例以指针的形式传递给run函数:
// 下面调用void QImage::invertPixels(InvertMode mode)
QImage image = ...;
QFuture<void> future = QtConcurrent::run(&QImage::invertPixels, &image, QImage::InvertRgba);
...
future.waitForFinished();

场景5: 传引用参数

下面的例子中, 使用std::ref来表明引用:

1
2
3
4
5
static void addOne(int &n) { ++n; }
...
int n = 42;
QtConcurrent::run(&addOne, std::ref(n)).waitForFinished(); // n == 43

场景6: 使用Callable对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 定义一个callable对象, 它会修改其状态.
struct TestClass
{
void operator()(int s1) { s = s1; }
int s = 42;
};

...

TestClass o;

// Modify original object
QtConcurrent::run(std::ref(o), 15).waitForFinished(); // o.s == 15

// Modify a copy of the original object
QtConcurrent::run(o, 42).waitForFinished(); // o.s == 15

// Use a temporary object
QtConcurrent::run(TestClass(), 42).waitForFinished();

// Ill-formed
QtConcurrent::run(&o, 42).waitForFinished(); // compilation error

带Promise的run

被执行的函数要带一个额外的QPromise<T>引用类型的参数, 而T是计算结果类型.

1
2
3
4
5
6

// 定义方法:
extern void foo(QPromise<void> &promise);

QFuture<void> future = QtConcurrent::run(foo);

promise参数会由run函数初始化, 并将其引用传递给线程函数, 因此, 调用时不需要定义和使用这个参数:

1
2
3
4
5
6
7
8

extern void foo(QPromise<void>& promise, int arg1, double arg2);
int arg1 = ...;
double arg2 = ...;

// 注意这里对foo()的调用, 它的第一个参数promise并不需要提供给run.
QFuture<void> future = QtConcurrent::run(foo, arg1, arg2);

执行中报告结果

Promise模式下的函数始终是返回void类型, 它的结果是通过promise的addResult来返回的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void foo(QPromise<QString>& promise)
{
...
promise.addResult("Hellow");
...
promise.addResult("World");

}

QFuture<QString> future = QtConcurrent::run(foo);
...
auto results = future.results();
...

挂起和取消执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void foo(QPromise<int> &promise)
{
for (int i = 0; i < 100; ++i) {
promise.suspendIfRequested();
if (promise.isCanceled())
return;

// computes the next result, may be time consuming like 1 second
const int res = ... ;
promise.addResult(res);
}
}

QFuture<int> future = QtConcurrent::run(foo);

... // user pressed a pause button after 10 seconds
future.suspend();

... // user pressed a resume button after 10 seconds
future.resume();

... // user pressed a cancel button after 10 seconds
future.cancel();

上报中间结果

它使用promisesetProgressValue()函数来设置进度, 并且使用QFutureWatcher::progressValueChange信号.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void foo(QPromise<int> &promise)
{
promise.setProgressRange(0, 100);
int result = 0;
for (int i = 0; i < 100; ++i) {
// computes some part of the task
const int part = ... ;
result += part;
promise.setProgressValue(i);
}
promise.addResult(result);
}

QFutureWatcher<int> watcher;
QObject::connect(&watcher, &QFutureWatcher::progressValueChanged, [](int progress){
... ; // update GUI with a progress
qDebug() << "current progress:" << progress;
});
watcher.setFuture(QtConcurrent::run(foo));