概述
在使用多线程时,我们经常会子类化QThread
或将QObject``moveToThread
来实现。
而Qt
中提供了一种高级方法,即QtConcurrent
。QtConcurrent
模块提供了包括run
,map
与filter
函数。
Concurrent Run
函数原型:
1 | // (1) |
原型(1)
的效果即为在另一个线程中运行function
函数,缺省的为参数,即:
1 | extern void fun(int i, double d, const QString& str) |
而原型(2)
则在(1)
的基础上增加了一个线程池的指针参数,即可以传入一个专有线程池用于执行函数。
返回值
由函数原型可以看到返回值为QFuture
模板变量,则可以使用以下方式获取返回值:
1 | extern QString fun(); |
但是注意,QFuture::result()
会阻塞当前线程,直到结果可用。
可以使用QFutureWatcher
监测该QFuture
直到发出finished
信号,且QtConcurrent
返回的QFuture
不支持canceled
与paused
。
其他特性
QtConcurrent::run
还可以接收成员函数的指针,常量成员函数一般传递常量引用,而非常量成员函数一般传递指针,如下:
1 | // run byteArray.split(',') in other thread |
- 也可以使用
Lambda
表达式作为参数:
1 | QFuture<void> future = QtConcurrent::run([=]()->void |
Concurrent Map and Map-Reduce
map
相关的函数主要应用场景是在单独的线程里对容器中的每一项进行操作。这些函数主要分为三类:
QtConcurrent::map
:直接操作容器中的每一项。QtConcurrent::mapped
:操作容器中的每一项,将处理结果返回一个新的容器,原容器不变。QtConcurrent::mappedReduced
:在mapped
的基础上将处理结果进一步传递给一个函数继续处理。
QtConcurrent::map
函数原型:
1 | // (1) |
原型(1)
直接传入一个容器,对其中每个元素执行function
,而原型(2)
则是传入迭代器,执行相同操作。
注意
- 传入的
function
原型必须是U function(T &t)
的形式(U
与T
可以是任何类型,但是T
必须与传入的容器存储的类型相同)。 - 因为是直接修改,所以该函数不会返回任何
QFuture
结果,但仍然可以使用QFutureWatcher
来监视返回的QFuture
:
1 | void add(int &number) |
QtConcurrent::mapped
函数原型:
1 | // (1) |
两个原型的差别与上面相同,(1)是传入容器,(2)传入的是迭代器。且都是为容器内每个对象执行函数,但不会改变容器的每一项,会将结果返回到一个新的容器。
注意
- 传入的
function
原型必须是U function(const T &t)
的形式(U
与T
可以是任何类型,但是T
必须与传入的容器存储的类型相同); - 返回的结果将会存储到
QFuture
中,可以使用QFuture::const_iterator
或QFutureIterator
访问:
1 | int add(const int &number) |
QtConcurrent::mappedReduced
函数原型:
1 | // (1) |
原型的差异依旧与上面相同,一个传入容器,一个则是迭代器。且两者的内部操作都是对传入的容器中对象执行完mapFunctor
的结果,传入ReduceFunctor
中再执行一次。
注意
- 传入的
mapFunction
需满足U function(const T &t)
的形式(U
与T
可以是任何类型,但是T
必须与传入的容器存储的类型相同); - 而
reduceFunction
则需要满足V function(T &result, const U &intermediate)
格式,T
为最终结果类型,U
是mapFunction
返回的类型,而V
则是没有使用到的类型和返回值; reduceOptions
则有三种:QtConcurrent::UnorderedReduce
= 0x1:以任意顺序完成;QtConcurrent::OrderedReduce
= 0x2:按原始序列的顺序完成;QtConcurrent::SequentialReduce
= 0x3:按顺序完成:一次只有一个线程进入reduce
函数。
在下面的代码中,原始数据list
的每一项都被fun1
函数处理,再将结果传到fun2
函数继续处理,其中intermedia
就是上一步的结果,最终会以result
这个变量输出:
1 | int fun1(const int &number) |
Concurrent Filter and Filter-Reduce
filter
相关的函数也与map
相关函数类似,也是对容器中元素进行处理,只是filter
注重筛选元素。也是分为三种:
QtConcurrent::filter
:直接对容器进行筛选,结果反馈到容器中;QtConcurrent::filtered
:对容器进行筛选,但结果以新容器返回;QtConcurrent::filteredReduced
:在filtered
的基础上,将筛选后的结果处理为单一的值。
QtConcurrent::filter
函数原型:
1 | template <typename Sequence, typename KeepFunctor> |
filter
仅支持传入容器而不支持迭代器(Qt5.15),对每个对象执行filterFunciton
,根据返回值决定对象的去留。
注意
传入的filterFunction
仅支持bool function(const T &t)
格式,T
必须与容器存储的对象类型匹配,如果要保留则返回true
,反之则返回false
:
1 | bool fun(const int &number) |
QtConcurrent::filtered
函数原型:
1 | // (1) |
与上面类似,filtered
的两个函数原型分别是传入容器和迭代器的差异。两者都会将容器内对象进行筛选,并将结果返回到新容器当中。
注意
传入的filterFunction
与filter
的一致,都仅支持bool function(const T &t)
格式,T
必须与容器存储的对象类型匹配,如果要保留到新容器则返回true
,反之则返回false
:
1 | bool fun(const int &number) |
QtConcurrent::filteredReduced
函数原型:
1 | // (1) |
filteredReduced
也有两个原型,差异在于容器与迭代器,不过多赘述。此函数会将容器中的对象先通过filterFunction
,再将这些结果通过reduceFunction
,最终反馈到返回的QFuture
中。
注意
- 传入的
filterFunction
原型与filter
和filtered
一致; - 传入的
reduceFunction
原型为V function(T &result, const U &intermediate)
,T
为最终结果类型,U
是mapFunction
返回的类型,而V
则是没有使用到的类型和返回值; reduceOptions
与mappedReduced
一致,不过多赘述,下面看示例:
1 | bool fun1(const int &number) |
Blocking
上述包括map
,mapped
,mappedReduced
,filter
,filtered
,filteredReduced
都是在另外一个线程中执行的。因为操作是异步的,所以不会立即返回值,如果立即去使用容器,则会出现:
- 无变化:
map
,filter
还未对原容器执行操作; - 指针越界:
mapped
,mappedReduced
,filtered
,filteredReduced
操作未完成,返回为空。
使用以上函数时需要对返回值进行判断:
1 | ... |
上面的函数都有blocking
的写法,即等待结果返回后再执行下一步:
1 | QFuture<int> future = QtConcurrent::blockingMap(list, fun); |
blocking
系列包括:
QtConcurrent::blockingMap
;QtConcurrent::blockingMapped
;QtConcurrent::blockingMappedReduced
;QtConcurrent::blockingfilter
;QtConcurrent::blockingfiltered
;QtConcurrent::blockingfilteredReduced
。
使用方法与非blocking
一致