Qt,C++,函数并行执行(异步编程)
·
0. 核心内容
// ************ 写入Pro文件 ************ //
// 有UI: QT += core gui widgets concurrent
// 静态库: QT += core concurrent
// ************ 头文件引入 ************ //
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrent> // 核心需求
#include <QElapsedTimer> // 耗时监控
// ************ 核心代码 ************ //
// 单形参,有返回值的处理函数
cv::Mat processFunc(const cv::Mat& mat)
{
cv::Mat resMat = mat.clone();
resMat .setTo(255, mat>0);
return resMat;
}
// 待并行处理的数据
vector<cv::Mat> datalist
// 并行处理
QFuture<cv::Mat> future = QtConcurrent::mapped(datalist, processFunc);
future.waitForFinished();
vector<cv::Mat> res = future.results().toVector().toStdVector();
1. 基础:单形参函数并行处理 vector<cv::Mat>
1.1 要求
- 处理cv::Mat的函数必须是以cv::Mat作为唯一形参传入,且函数返回值也是cv::Mat的,比如:
cv::Mat processMat(const cv::Mat& mat) { cv::Mat result; cv::GaussianBlur(mat, result, cv::Size(5,5), 1.5); // 高斯模糊示例 return result; } - 处理的对象是 vector<cv::Mat>
- 对cv::Mat的处理必须彼此独立互不干扰
1.2 目的
对vector<cv::Mat>由for循环的串行处理升级为Qt下的并行处理,提升算法速度。
1.3 代码
在pro文件中进行如下配置
// 涉及UI或widget文件
QT += core gui widgets concurrent
// 涉及UI或widget文件
QT += core concurrent
在源文件中这样构造
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrent> // 核心需求
#include <QElapsedTimer> // 耗时监控
// 签名:输入const cv::Mat&,返回cv::Mat → 所有符合该签名的函数都可传入
using FuncMat2Mat = std::function<cv::Mat(const cv::Mat&)>;
// cv::Mat 处理函数
cv::Mat processMat(const cv::Mat& mat) {
cv::Mat result;
cv::GaussianBlur(mat, result, cv::Size(5,5), 1.5); // 高斯模糊示例
return result;
}
// 串行处理方案
vector<cv::Mat> processSerial(const vector<cv::Mat>& datalist, FuncMat2Mat processFunc) {
vector<cv::Mat> reslist;
reslist.reserve(datalist.size());
for (const auto& mat : datalist) {
reslist.push_back(processFunc(mat));
}
return reslist;
}
// 并行处理方案
vector<cv::Mat> processParallel(const vector<cv::Mat>& datalist, FuncMat2Mat processFunc) {
// QtConcurrent::mapped 自动将datalist的每个元素并行传入processMat
QFuture<cv::Mat> future = QtConcurrent::mapped(datalist, processFunc);
future.waitForFinished(); // 等待所有并行任务完成
return future.results().toVector().toStdVector();
}
// 测试函数
void testFunction()
{
// 1. 构造测试数据:生成10个随机图像
vector<cv::Mat> datalist;
for (int i = 0; i < 10; ++i) {
cv::Mat mat(800, 800, CV_8UC3, cv::Scalar(rand()%255, rand()%255, rand()%255));
datalist.push_back(mat);
}
// 2. 测试串行耗时
QElapsedTimer timer;
timer.start();
vector<cv::Mat> resSerial = processSerial(datalist, processMat);
qDebug() << "Serial Times=" << timer.elapsed() << "ms";
// 3. 测试并行耗时
timer.restart();
vector<cv::Mat> resParallel = processParallel(datalist, processMat);
qDebug() << "Parallel Times=" << timer.elapsed() << "ms";
// 验证结果数量一致(确保并行处理无遗漏)
qDebug() << "Serial num=" << resSerial.size() << ", Parallel num=" << resParallel.size();
}
2. 进阶:多形参函数并行处理 vector<cv::Mat>
2.1 说明
QtConcurrent::mapped 函数前后两个输入分别是:
- sequence:例如vector,里面的数据类型不限(QList,QVector等亦可)
- map:一个函数名,该函数只能有一个输入形参,且数据类型和构成vector的一致
2.2 目的
由于QtConcurrent::mapped函数只能调用有一个输入形参的函数,因此当某个函数需要多个输入形参时,例如:
cv::Mat processParaMat(const cv::Mat& mat, int size, int scale)
{
cv::Mat result;
cv::GaussianBlur(paraMat.mat, result, cv::Size(size, size), scale);
return result;
}
此时,为了还能调用QtConcurrent::mapped函数并行处理,需要通过构造结构体改造一下processParaMat函数,如下:
struct ParaMat
{
cv::Mat mat;
int para1;
int para2;
};
cv::Mat processParaMat(const ParaMat& paraMat)
{
cv::Mat result;
cv::GaussianBlur(paraMat.mat, result, cv::Size(paraMat.para1, paraMat.para1), paraMat.para2);
return result;
}
这样,就能把参数信息和cv::Mat信息合并到一个结构体中,通过单形参函数处理了。
完整代码:
struct ParaMat
{
cv::Mat mat;
int para1;
int para2;
};
cv::Mat processParaMat(const ParaMat& paraMat)
{
cv::Mat result;
cv::GaussianBlur(paraMat.mat, result, cv::Size(paraMat.para1, paraMat.para1), paraMat.para2);
return result;
}
vector<cv::Mat> processParallel(const vector<ParaMat>& datalist) {
QFuture<cv::Mat> future = QtConcurrent::mapped(datalist, processParaMat);
future.waitForFinished(); // 等待所有并行任务完成
return future.results().toVector().toStdVector();
}
void testFunction()
{
// 1. 构造测试数据
vector<ParaMat> datalist;
for (int i = 0; i < 10; ++i) {
ParaMat data;
data.mat = cv::Mat(800, 800, CV_8UC3, cv::Scalar(rand()%255, rand()%255, rand()%255));
data.para1 = 7;
data.para2 = 7;
datalist.push_back(data);
}
// 2. 测试并行耗时
QElapsedTimer timer;
timer.start();
vector<cv::Mat> res = processParallel(datalist);
qDebug() << "Parallel Times=" << timer.elapsed() << "ms";
// 验证结果数量一致(确保并行处理无遗漏)
qDebug()<<", Parallel num=" << res.size();
}
3. 进阶:类似的并行函数
3.1 QtConcurrent::map()
适用于:单形参且无返回值的void函数来处理输入信息(变量原地址赋值)
// 单形参,无返回值的void处理函数
void processFunc(cv::Mat& mat)
{
mat.setTo(255, mat>0);
}
// 待并行处理的数据
vector<cv::Mat> datalist
// 并行处理
QFuture<cv::Mat> future = QtConcurrent::map(datalist, processFunc);
future.waitForFinished();
vector<cv::Mat> res = future.results().toVector().toStdVector();
3.2 QtConcurrent::mapped()
适用于:单形参且有返回值的函数来处理输入信息(创建新变量返回)
// 单形参,有返回值的处理函数
cv::Mat processFunc(const cv::Mat& mat)
{
cv::Mat resMat = mat.clone();
resMat .setTo(255, mat>0);
return resMat;
}
// 待并行处理的数据
vector<cv::Mat> datalist
// 并行处理
QFuture<cv::Mat> future = QtConcurrent::mapped(datalist, processFunc);
future.waitForFinished();
vector<cv::Mat> res = future.results().toVector().toStdVector();
3.3 QtConcurrent::mappedReduced()
适用于
- 单形参且有返回值的函数来处理输入信息(创建新变量返回)
- 处理后的信息按照有序或无序的方式迭代处理,并最终返回
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <vector>
#include <opencv2/opencv.hpp>
// ========== 1. 映射函数:处理Mat(高斯模糊) ==========
cv::Mat processMat(const cv::Mat& mat) {
cv::Mat result;
cv::GaussianBlur(mat, result, cv::Size(5,5), 1.5);
return result;
}
// ========== 2. 归约函数:拼接所有Mat为一个大图 ==========
void concatMats(cv::Mat& finalMat, const cv::Mat& singleMat) {
if (finalMat.empty()) {
finalMat = singleMat.clone(); // 第一个Mat直接赋值
} else {
// 横向拼接:最终结果 = 原最终结果 + 当前Mat
cv::hconcat(finalMat, singleMat, finalMat);
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 构造测试数据:2个相同大小的随机Mat
std::vector<cv::Mat> datalist;
datalist.emplace_back(400, 400, CV_8UC3, cv::Scalar(0,255,0)); // 绿色
datalist.emplace_back(400, 400, CV_8UC3, cv::Scalar(255,0,0)); // 红色
// ========== 3. 并行处理+归约(拼接) ==========
QFuture<cv::Mat> future = QtConcurrent::mappedReduced(
datalist,
processMat,
concatMats,
QtConcurrent::OrderedReduce // 按顺序拼接(保证绿色在前,红色在后)
);
future.waitForFinished();
// 保存最终拼接图
cv::Mat finalMat = future.result();
cv::imwrite("concat_result.png", finalMat);
qDebug() << "拼接图尺寸:" << finalMat.rows << "x" << finalMat.cols; // 400x800
return 0;
}
注意:
-
最终结果的初始化:
reduceFunc的第一个参数(最终结果)会被 QtConcurrent 自动初始化为 “默认值”(如int初始为 0,cv::Mat初始为空),无需手动初始化。 -
OrderedReduce vs UnorderedReduce:
- 若聚合结果和顺序无关(如统计总数、求和),用默认的
UnorderedReduce(效率更高); - 若聚合结果依赖顺序(如拼接图像、按输入顺序汇总),用
OrderedReduce。
- 若聚合结果和顺序无关(如统计总数、求和),用默认的
更多推荐
所有评论(0)