QtConcurrent命名空间提供了高级API,使得可以在不使用低级线程原语(例如:互斥、读写锁、等待条件或信号量)的情况下编写多线程程序,例如子类化QThread、QObject::moveToThread()、子类化QRunnable对于共享数据的保护都要使用低级线程原语,这无疑是要非常小心的。
使用 QtConcurrent 编写的程序根据可用的处理器核心数自动调整所使用的线程数。这意味着,当在未来部署多核系统时,现在编写的应用程序将继续适应。
当你发现你自己的程序UI运行不流畅时可以尝试将执行计算的函数放到QtConcurrent::run()中处理,这比改用QThread方便得多。
( 以前处理方式都采用moveToThread开启线程处理,最近新看到新处理方式QtConcurrent::run来启动新线程)
原来moveToThread方式实现:
1.定义一个继承QObject的类A;
2.处理事务逻辑函数假设为doWork();
3.将类A的实例的对象a调用moveToThread(thread),QThread实例thread;
4.绑定信号槽connect(thread, &QThread::started, a, &A::doWork);
5.启动线程thread.start()。
QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
function函数会在一个单独线程中进行,并且该线程取自全局QThreadPool。
返回的QFuture可以用来查询函数是否运行、完成状态和function的返回值。
(该函数需要用到Concurrent模块)
返回的QFuture就可以使用QFutureWatch监视,当QFuture的状态发生改变时,监视者就可以监视这个回调从而对异步运行的结果进行处理。
QtConcurrent的常用方法有QtConcurrent::run()
、QtConcurrent::map()
、QtConcurrent::filter()
。
map和filter还有其衍生的方法,例如还有mapped()、mappedReduced()、filtered()、filteredReduced。下面通过使用这几个方法来了解QtConcurrent的使用方法。
run方法是通过另开一个线程来执行用户指定的函数,需要注意的是这个线程是在线程池中获取的,也就是说这个线程是不需要手动释放的,运行完指定的函数线程会自动释放。
//自定义函数
QString hello(QString name,QString name1,QString name2,QString name3){
qDebug() << "hello" <<name << "from" <<QThread::currentThread();
for(int i=0; i<3; i++){
QThread::sleep(1);
qDebug("[%s] i = %d",name.data(),i);
}
return name+name1+name2+name3;
}
//run函数在线程中调用自定义的函数,后面可以跟函数的参数(可选),QFuture能够获取到函数执行的返回值,returnType必须和函数的返回类型一致
QFuture<QString> f1 = QtConcurrent::run(hello,QString("Alice"),QString("Alice"),QString("Alice"),QString("Alice"));
QFuture<QString> f2 = QtConcurrent::run(hello,QString("Bob"),QString("Bob"),QString("Bob"),QString("Bob"));
//QFuture::result()获取单个返回值
qDebug() << f1.result();
qDebug() << f2.result();
//等待结束释放
f1.waitForFinished();
f2.waitForFinished();
map 函数用于需要更改原容器中数据的使用场景,对容器中的每个项目都调用一次函数,且每次调用都是单独的一个线程。这个没有返回新容器,所以不能通过future获取结果。线程也来自线程池,和run方法类似,不需要手动释放。
QtConcurrent::map()
:对序列的每一项元素都应用一个函数,并将运算结果替换原来的元素。QtConcurrent::mapped()
:功能类似 map() 函数,它会返回一个新容器存储函数处理后的结果。QtConcurrent::mappedReduced()
:类似于 mapped() ,他会将这个返回的结果序列,经过另一个函数处理为一个单个的值。 void toUpperMap(QString &str)
{
str = str.toUpper();
}
QStringList strWords;
strWords << "Apple" << "Banana" << "cow" << "dog" << "Egg";
//第一个参数是原容器,第二参数是每个项目需要调用的方法
auto future = QtConcurrent::map(strWords,toUpperMap);
future.waitForFinished();
qDebug() << strWords;
//输出:QList("APPLE", "BANANA", "COW", "DOG", "EGG")
QString toUperrMapped(const QString &str){
return str.toUpper();
}
QStringList oldStr;
oldStr << "Apple" << "Banana" << "cow" << "dog" << "Egg";
auto future = QtConcurrent::mapped(oldStr,toUperrMapped);
future.waitForFinished();
qDebug() << future.results();//results()返回全部数据,这里如果调用result()只会返回一个数据,即APPLE。
//输出:QList("APPLE", "BANANA", "COW", "DOG", "EGG")
filter函数与map函数类似,其衍生函数也与map的衍生函数类似,只是函数用于过滤。
void reduceFun(QList<QString> &dictionary,const QString &string){
dictionary.push_back(QString("result:")+string);
}
bool fiter (QString string){
if(string.length()>3){ //只要长度大于3的字符串
return true;
}else return false;
}
QStringList oldStrfilter;
oldStrfilter << "Apple" << "Banana" << "cow" << "dog" << "Egg";
auto future1 = QtConcurrent::filter(oldStrfilter,fiter);
//filtered函数与mapped函数类似,只是函数用于过滤
auto future2 = QtConcurrent::filtered(oldStrfilter,fiter);
//filteredReduced函数与mappedReduced函数类似,只是函数用于过滤
auto future3 = QtConcurrent::filteredReduced(oldStrfilter,fiter,reduceFun);
future1.waitForFinished();
future2.waitForFinished();
future3.waitForFinished();
qDebug() << oldStrfilter;
qDebug() << future2.results();
qDebug() << future3.results();
/**
输出:
QList("Apple", "Banana")
QList("Apple", "Banana")
QList(QList("result:Apple", "result:Banana"))
**/
QtConcurrent::run
,在线程池内起一个线程来执行一个函数。
QtConcurrent::map
, 用于并行处理一批数据的场景。
QtConcurrent::filter
,一般用于对一批数据的过滤操作。
使用QtConcurrent处理一些需要在并行线程中完成的计算,比使用QThread更方便(使用QThread要自己加锁的)。