Qt - 多线程之并发(QtConcurrent)
一、什么是QtConcurrent?
Concurrent是并发的意思,而QtConcurrent同std一样,是一个命名空间(namespace)。提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。
对于QtConcurrent真正要学习的是该命名空间下定义的函数。下面要讲的就是QtConcurrent::run函数的使用方法。
二、QtConcurrent::run()
QT有几种可以实现多线程编程的方式,其中最方便使用,最便携的一定是QtConcurrent::run()了,这是一个模板函数,有很多的重载原型。
//在新的线程中调用普通函数
template <typename T> QFuture<T> QtConcurrent::run(Function function, ...)
//在新的线程里调用成员函数 有多个重载实现不同的参数个数
template <typename T> QFuture<T> QtConcurrent::run(className *obejct, Function function, ...)
//使用线程池中的线程调用普通函数
template <typename T> QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
这些模板都有多个重载,支持带参数函数,切可以带多个参数。
QFuture 也是一个模板类,它可以监控线程的运行状态有,并可以获取在另一个线程中运行的函数的返回值,返回值通过调用result()函数获取。
需要注意的是获取返回值之前,最好先调用waitForFinished()保证线程执行完毕。
注意:使用QtConcurrent::run()需要在.pro文件中添加该模块并且包含头文件
.pro文件中添加:
QT += concurrent
包含头文件:
#include <QtConcurrent>
三、QtConcurrent::run()运行示例
每调用一次QtConcurrent::run()函数,就新建立一个线程。
示例1:将普通函数运行在两个不同的线程中
#include <QString>
#include <QDebug>
#include <QThread>
#include <QApplication>
#include "qtconcurrentrun.h"
QString func1()
{
qDebug()<<"我是func2函数";
}
Qstring func2(QString name)
{
qDebug() << name << "from" << QThread::currentThread();
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QFuture<QString> fut1 = QtConcurrent::run(func1);// 1.用QFuture获取该函数的运行结果
QFuture<QString> fut2 = QtConcurrent::run(func2, QString("Thread 1"));//2.参数2:向func函数传递的参数
QString result1 = fut1.result();
QString result2 = fut2.result();
fut1.waitForFinished();
fut2.waitForFinished();
}
运行结果:
//输出结果如下:
"Thread 1" from QThread(0x1b74fd2ebc0, name = "Thread (pooled)")
"Thread 2" from QThread(0x1b74fd534e0, name = "Thread (pooled)")
示例2:成员函数
- 将类中的成员函数运行在某一个线程中,可将指向该类实例的 引用或指针 作为 QtConcurrent::run 的第一个参数传递进去;
- 常量成员函数传递常量引用 (const reference),而非常量成员函数一般传递指针 (pointer)
#include <QString>
#include <QDebug>
#include <QThread>
#include <QApplication>
#include "qtconcurrentrun.h"
class A
{
public:
A()
{
std::cout << "我是 A 的构造函数" << std::endl;
};
QString m_func(QString name)
{
m_name = name;
std::cout << "我是 A 的成员函数" << std::endl;
}
QString m_name;
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QByteArray bytearray = "hello world";
A *a = new A();
QFuture<QString> fut2 = QtConcurrent::run(bytearray,&QByteArray::split,',');//1.调用QByteArray的常量成员函数split(),传递常量引用,bytearray
QFuture<QByteArray> fut3 = QtConcurrent::run(a,&A::m_func, QString("Thread 2"));//2.非常量成员函数运行在一个新的线程,传递指针
QString result2 = fut2.result();
QString result3 = fut3.result();
fut2.waitForFinished();
fut3.waitForFinished();
}
运行结果:
//输出结果如下:
"Thread 1" from QThread(0x1b74fd2ebc0, name = "Thread (pooled)")
"Thread 2" from QThread(0x1b74fd534e0, name = "Thread (pooled)")
示例3:线程池的应用
如果要为其指定线程池,可以将线程池的指针作为第一个参数传递进去
#include <QString>
#include <QDebug>
#include <QThread>
#include <QApplication>
#include "qtconcurrentrun.h"
using namespace QtConcurrent;
void func(QString name)
{
qDebug() << name << "from" << QThread::currentThread();
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QThreadPool pool;
pool->setMaxThreadCount(QThreadPool::globalInstance()->maxThreadCount());//setMaxThreadCount设置最大线程数,maxThreadCount()获取该计算机可运行的线程数
QFuture<void> fut1 = run(&pool,func, QString("Thread 1"));
fut1.waitForFinished();
}
四、总结
- 调用run() 之后,函数不一定会被立即执行,如果有多个run()被调用,函数的调用顺序不一定是run()的调用顺序,这些都和线程的调度有关系。
- run(function) 实际上等价于run(QThreadPool::globalInstance(),function)
如果只是简单的想在其他线程中调用某个函数,不需要复杂的数据同步,那么QtConcurrent::run() 相比其他实现多线程的方式绝对是不二之选。