跳转到内容
新建笔记

QtConcurrent:函数任务、映射归约与完成通知

QtConcurrent 把可调用对象或序列算法提交给线程池,用 QFuture 表达异步结果。它适合独立计算,不会让 QObject 自动改变线程归属,也不要求普通 C++ 类继承 QObject。

本页使用 Qt 6、C++17 接口,示例在 Qt 6.10.2 验证。成员函数参数顺序与部分 Qt 5 示例不同,应先确认项目版本。

Qt 6 的常用形式是 run(function, arguments...);使用独立池时是 run(&pool, function, arguments...)。成员函数指针放在函数位置,其后才是实例或实例指针。

调用含义
run(work, 42, text)调用普通函数并传参
run(&Calculator::multiply, &calculator, 6)对指定实例调用成员函数
run(&QByteArray::split, data, ',')把值对象交给 const 成员函数调用
run(&pool, work, 42, text)使用专用线程池

参数通常会被复制到任务中;传入指针仍是复制指针值,不是复制指向的对象。因此实例、缓冲区以及被引用数据必须活到任务结束,并避免未同步的并发修改。Qt Concurrent Run

完整例子:普通函数、成员函数和映射归约

跳转到“完整例子:普通函数、成员函数和映射归约”

这个程序是命令行验证,允许阻塞等待。GUI 的正常操作期间应使用下一节的完成通知。

// main.cpp;链接 Qt6::Core、Qt6::Gui、Qt6::Concurrent
#include <QByteArray>
#include <QCoreApplication>
#include <QFuture>
#include <QImage>
#include <QList>
#include <QString>
#include <QThreadPool>
#include <QtConcurrent>
#include <cassert>
int work(int value, const QString &text) { return value + text.size(); }
class Calculator { // 不继承 QObject
public:
int multiply(int value) const { return value * 7; }
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
QThreadPool pool;
pool.setMaxThreadCount(2);
auto plain = QtConcurrent::run(&pool, work, 40, QStringLiteral("ok"));
const int plainResult = plain.result();
assert(plainResult == 42);
Calculator calculator;
auto member = QtConcurrent::run(&Calculator::multiply, &calculator, 6);
const int memberResult = member.result();
assert(memberResult == 42);
auto split = QtConcurrent::run(&QByteArray::split, QByteArray("a,b"), ',');
const auto splitResult = split.result();
assert((splitResult == QList<QByteArray>{"a", "b"}));
QImage image(1, 1, QImage::Format_ARGB32);
image.fill(qRgb(0, 0, 0));
auto invert = QtConcurrent::run(&QImage::invertPixels, &image, QImage::InvertRgb);
invert.waitForFinished();
assert(image.pixel(0, 0) == qRgb(255, 255, 255));
const QList<int> values{1, 2, 3, 4, 5};
QList<int> inPlace = values;
auto mappedInPlace = QtConcurrent::map(inPlace, [](int &value) { value *= 2; });
mappedInPlace.waitForFinished();
assert((inPlace == QList<int>{2, 4, 6, 8, 10}));
QtConcurrent::blockingMap(inPlace, [](int &value) { value /= 2; });
assert(inPlace == values);
QFuture<int> mapped = QtConcurrent::mapped(values, [](int value) { return value * 2; });
const QList<int> results = mapped.results();
assert((results == QList<int>{2, 4, 6, 8, 10}));
auto blocking = QtConcurrent::blockingMapped<QList<int>>(
values, [](int value) { return value * 2; });
assert(blocking == results);
QFuture<int> reduced = QtConcurrent::mappedReduced<int>(values,
[](int value) { return value * 2; },
[](int &sum, int value) { sum += value; },
QtConcurrent::OrderedReduce | QtConcurrent::SequentialReduce);
const int sum = reduced.result();
assert(sum == 30);
}

请注意 QFuture 的模板参数:mapped 每项输出是 int,所以类型是 QFuture<int>,用 results() 获取所有结果;不是 QFuture<QList<int>>。后者表达的是某个任务结果本身为一个 QList,例如 run 调用返回 QList 的函数。

算法是否修改原序列返回内容
map是,回调接收元素引用QFuture<void>
blockingMap是完成后返回,无 future
mapped否含多个映射结果的 QFuture<T>
blockingMapped否新序列
mappedReduced否归约后的单个结果

reduce 回调通过 Result& 修改累加器,不能把它误写成接收值副本并返回新值。示例明确选择按原序列顺序、串行调用 reducer;map 阶段仍可并行。若算法依赖归约顺序,应显式表达,不能依赖任务完成的偶然先后。Qt 映射与归约

QFutureWatcher:保留事件循环接收完成通知

跳转到“QFutureWatcher:保留事件循环接收完成通知”
// main.cpp;链接 Qt6::Core、Qt6::Concurrent
#include <QCoreApplication>
#include <QFutureWatcher>
#include <QThread>
#include <QtConcurrent>
#include <cassert>
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
QFutureWatcher<int> watcher;
int received = 0;
QObject::connect(&watcher, &QFutureWatcher<int>::finished, &app, [&] {
assert(QThread::currentThread() == app.thread());
received = watcher.result(); // finished 后此结果已就绪
app.quit();
});
watcher.setFuture(QtConcurrent::run([] { return 42; }));
const int exitCode = app.exec();
assert(received == 42);
return exitCode;
}

先建立连接,再设置 future;watcher 必须活到任务完成,所属线程需要处理事件。创建一个局部 watcher 后立即从 main 返回,不能可靠接收异步完成通知。QFutureWatcher

result()、results() 和 waitForFinished() 都可能阻塞。future 只管理结果状态,不替你维持外部对象的寿命;watcher 消失也不意味着任务被强行终止。

普通 QtConcurrent::run 不提供任意位置的强制取消;需要协作暂停、取消或报告多条结果时,可研究 Qt 6 的 QPromise 模式,并让任务主动检查请求。可能抛异常的任务还应在接收结果处处理相应失败,不能把 finished 一律视为业务成功。

原始记录:CSDN。线程选型见 QThread 与线程池。