之前的文章我们都是讨论 QThread 这种很底层的类,使用起来要考虑方方面面。或者说只要用到底层的接口(如 QThread、信号量、互斥锁等),那基本就是重新造一个轮子出来。幸运的是,Qt 提供了高级函数来简化我们的多线程编写,它就是 Qt Concurrent 模块!
只要在 pro 文件添加“Qt += concurrent”并且在我们的 h 文件添加“#include ”,就可以使用这些函数了。基本上所有的 concurrent 函数分为三种类型:
run
相关:执行函数用;map
相关:处理容器中的每一项;filter
相关:筛选容器中的每一项。使用最多的差不多就是这个了,比如我要在一个单独的线程里执行某个函数,只要写一行就可以了:
#include
#include
#include
void function()
{
qDebug() << "Hello world";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QtConcurrent::run(function); //调用 run() 函数就可以在单独线程里运行
return a.exec();
}
简单到哭…
直接在函数名后面跟上参数即可,例如:
extern void aFunction(int arg1, double arg2, const QString &string);
int a = 1;
double b = 2;
QString string = "hello";
QtConcurrent::run(aFunction, a, b, string);
传参数的时候,都会复制一份副本。所以即使参数是引用,在函数中修改数据也不会对源对象产生任何影响。
返回值都是通过 QFuture 来获取返回值。例如:
extern QString function();
QFuturen<QString> future = QtConcurrent::run(function);
QString result = future.result();
注意,QFuture::result()
函数会阻塞,直到结果可用。
QtConcurrent::run() 函数还可以接收成员函数的指针,例如:
QByteArray bytearray = "hello,world";
QFuture<QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
QList<QByteArray> result = future.result();
QtConcurrent::run() 函数也可以接收 Lambda 匿名函数,例如:
QFuture<void> future = QtConcurrent::run([=](){..Code..});
map 相关的函数主要应用场景是在单独的线程里对容器中的每一项进行操作。这些函数主要分为三类:
QtConcurrent::map()
:直接操作容器中的每一项。QtConcurrent::mapped()
:操作容器中的每一项,将处理结果返回一个新的容器,原容器不变。QtConcurrent::mappedReduced()
:在 mapped() 的基础上将处理结果进一步传递给一个函数继续处理。QtConcurrent::map() 可以直接修改容器的每一项,处理函数必须是“U function(T &t)”的形式。因为是直接修改,所以 map() 函数不会返回任何 QFuture 结果,但仍然可以使用 QFuture 和 QFutureWatcher 来监视状态。
示例:
#include
#include
#include
void function(int &num)
{
num = num * 10;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list = {1,2,3,4,5};
QtConcurrent::map(list, function);
return a.exec();
}
QtConcurrent::mapped() 不直接修改容器的每一项,而是将处理后的结果返回一个新的容器,处理函数必须是“U function(const T &t)”的形式。
示例:
#include
#include
#include
#include
int function(const int &num)
{
return num * 10;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list = {1,2,3,4,5};
QFuture<int> future = QtConcurrent::mapped(list, function);
QFutureIterator<int> i(future);
return a.exec();
}
注意上述代码,新的的结果用 QFuture::const_iterator 或 QFutureIterator 这种迭代器来访问。
QtConcurrent::mappedReduced() 类似于 mapped(),区别在于将结果继续传递给一个新函数,并在新函数里再处理成一个单值。新函数必须是“V function(T &result,const U &intermediate)”的形式。
示例:
#include
#include
#include
int function(const int &num)
{
return num * 10;
}
void reducedFunction(int &result, const int &intermedia)
{
result += intermedia;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list = {1,2,3,4,5};
QFuture<int> future = QtConcurrent::mappedReduced(list, function, reducedFunction);
qDebug() << result;
return a.exec();
}
在上述代码中,原始数据 list 中的每一项被 function() 函数处理。再将结果传给 reducedFunction() 函数继续处理,其中 intermedia 就是上一步的结果,最终以 result 这个变量输出。
filter 相关函数和 map 相关函数类似,也是对容器中的元素进行处理,但 filter 更多侧重筛选元素。和 map 一样也是分为三种类型:
QtConcurrent::filter() 函数直接操作容器,处理函数必须是“bool function(const T &t)”的形式。返回为 true 的元素会保留,反之会从容器中删除。
示例,保留大于3的元素:
#include
#include
bool function(const int &num)
{
return num > 3;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list = {1,2,3,4,5};
QtConcurrent::filter(list, function);
return a.exec();
}
QtConcurrent::filtered() 函数不直接操作容器,将结果以新容器返回,处理函数和 filter() 一样。
示例:
#include
#include
bool function(const int &num)
{
return num > 3;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list = {1,2,3,4,5};
QFuture<int> future = QtConcurrent::filtered(list, function);
return a.exec();
}
QtConcurrent::filteredReduced() 类似于 filtered(),将进一步把结果处理成一个单一值。处理函数的形式必须是“V function (T &result, const U &intermediate)”。
示例:将容器中大于3的值求和。思路很明显,第一步找出大于3的元素,第二步将这些元素求和。
#include
#include
bool function1(const int &num)
{
return num > 3;
}
void function2(int &result, const int &intermidiate)
{
result += intermidiate;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list = {1,2,3,4,5};
QFuture<int> future = QtConcurrent::filteredReduced(list, function1, function2);
qDebug() << future.result();
return a.exec();
}
讲到这里我们要明白,上面都是单独开了一个线程所执行的。如果同学们写自己的示例时,debug 的结果可能是容器中的元素并未改变,这是因为处理过程是异步的,它不是立即返回。上面的 map、filter 相关的函数都有 Blocking 的写法,就是等待结果可用时才会继续运行下去。
这些函数包括:
参考链接:
https://zhuanlan.zhihu.com/p/59125461