Qt多线程开发之Concurrent框架

引入

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()。

使用concurrent命名空间中的函数

QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)

function函数会在一个单独线程中进行,并且该线程取自全局QThreadPool。
返回的QFuture可以用来查询函数是否运行、完成状态和function的返回值。

(该函数需要用到Concurrent模块)

返回的QFuture就可以使用QFutureWatch监视,当QFuture的状态发生改变时,监视者就可以监视这个回调从而对异步运行的结果进行处理。

QtConcurrent基本使用

QtConcurrent的常用方法有QtConcurrent::run()QtConcurrent::map()QtConcurrent::filter()
map和filter还有其衍生的方法,例如还有mapped()、mappedReduced()、filtered()、filteredReduced。下面通过使用这几个方法来了解QtConcurrent的使用方法。

run方法

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方法

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函数

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要自己加锁的)。

你可能感兴趣的:(C++,qt,多线程)