QtConcurrent::run
QtConcurrent::run() 函数在单独的线程中运行一个函数,也就是说这是一种便捷的使用多线程的方法,实现异步运算。
使用QtConcurrent::run() 执行步骤如下:
- 添加模块 QT += concurrent
- 添加头文件
#include <QtConcurrent>
- 调用 QtConcurrent::run() 函数
1、QtConcurrent::run() 原型
QtConcurrent::run()的原型很多,但是最基本的是以下 4 种,返回类型是QFuture,返回的结果在QFuture里面。
- QFuture< T > run( Function function ),无参
- QFuture< T > run( Function function, const Arg1 &arg1 ⋯ \cdots ⋯ const Arg5 &arg5),有参,参数最多5个
- QFuture< T > run( const Class *object, Function function ⋯ \cdots ⋯) 成员函数
- QFuture< T > run(QThreadPool *pool, const Class *object, Function function ⋯ \cdots ⋯) 指定 QThreadPool 的成员函数
Function 是一个函数类型,由函数返回值和参数类型、个数决定。
2、同步和异步的效果对比
异步运行,主要是为了防止一些耗时的运算会阻塞当前线程,QtConcurrent::run 的出现使得异步执行变得轻快🏃
-
有一个耗时的计算过程 getCount()
void getCount(){ // 无参无返回值 int i = 0; while (i < 3) { qDebug() << i; Sleep(1000); // 1000 ms ++i; } return; }
-
同步运行会阻塞当前线程
#include <QApplication> #include <QDebug> #include <windows.h> void getCount(); int main(int argc, char *argv[]) { QApplication a(argc, argv); qDebug() << "start"; getCount (); // 耗时工作 qDebug() << "end"; return 0; }
结果如下:当前的主线程被阻塞,等待getCout函数执行完毕,才结束
-
使用QtConcurrent::run() 异步执行
- 添加模块 QT += concurrent
- 添加头文件
#include <QtConcurrent>
- 在main 里 调用 QtConcurrent::run()
int main(int argc, char *argv[]) { QApplication a(argc, argv); qDebug() << "start"; QtConcurrent::run( getCount ); qDebug() << "end"; return 0; }
运行结果,如下图:
可见在异步执行中,主线程从头到尾运行完毕,完全没有被耗时的工作阻塞。
3、传递参数
只要知道了 QtConcurrent::run() 原型,就可以很简单地调用,参数最多5个,如果多余5个,可用容器定义函数。
void getCount(int n){ // 有参无返回值
int i = 0;
while (i < n) {
qDebug() << i;
Sleep(1000); // 1000 ms
++i;
}
return;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug() << "start";
QtConcurrent::run( getCount, 4 );
qDebug() << "end";
return 0;
}
// 截图略
4、获取返回值
QtConcurrent::run() 的返回值存在 QFuture 中,需要调用 QFuture 的成员函数来获取 。
谨记:QFuture 的结果查询函数,都是阻塞型的,他们都会等待 QtConcurrent::run() 结束。
QFuture 结果查询的函数 | |
---|---|
T result() const | resultAt(0),阻塞型 |
T resultAt(int index) const | 阻塞型 |
resultCount() const | 结果数量,如果不阻塞,返回值不准确 |
QList< T > results() const | 结果集合,阻塞型 |
修改上面的代码,如下:
int getCount(int n){ // 有参有返回
int i = 0;
while (i < n) {
qDebug() << i;
Sleep(1000); // 300ms
++i;
}
return n;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug() << "start";
QFuture<int> f = QtConcurrent::run( getCount, 4 );
qDebug()<< f.result () << f.resultCount () ; // 会阻塞线程
qDebug() << "end";
return 0;
}
这么些,异步执行有啥意义呢?
5、使用QFutureWatcher的信号槽
QFuture 类本身未继承QObject,所以没法直接使用信号槽,所以需要另外一个监视类 QFutureWatcher。
使用步骤:
- 定义QFutureWatcher
QFutureWatcher<int> watcher;
- 设置监视对象 QFuture
watcher.setFuture(future);
- 使用QFutureWatcher信号槽
connect(&watcher, &QFutureWatcher<int>::finished ... );
代码如下:
#include <QApplication>
#include <QDebug>
#include <windows.h>
#include <QtConcurrent>
#include <QFutureWatcher>
int getCount(int n){ // 有参有返回
qDebug() << "start" << QThread::currentThread ();
int i = 0;
while (i < n) {
qDebug() << i;
Sleep(1000);
++i;
}
qDebug() << "end" << QThread::currentThread ();
return n;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug() << "start" << QThread::currentThread ();
QFuture<int> f = QtConcurrent::run( getCount, 4 );
QFutureWatcher<int> w;
w.setFuture (f);
QObject::connect (&w,&QFutureWatcher<int>::finished,[&]{
qDebug() << QThread::currentThread ();
qDebug()<< f.result () << f.resultCount () ;
a.quit (); // 主动退出,否则进程一直在循环
});
qDebug() << "end" << QThread::currentThread ();
return a.exec (); // 开启事件序列,等待信号槽的处理
}
结果如下:
6、调用成员函数
用法大同小异,只要使用 QtConcurrent::run()的第三个原型函数即可。
代码:
#include <QApplication>
#include <windows.h>
#include <QtConcurrent>
#include <QtCore>
class Test{ // 声明一个类
public:
int getCount(int n){
qDebug() << "start" << QThread::currentThread ();
int i = 0;
while (i < n) {
qDebug() << i;
Sleep(1000);
++i;
}
qDebug() << "end" << QThread::currentThread ();
return n;
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug() << "start" << QThread::currentThread ();
Test t; // 定义Test类对象
QFuture<int> f = QtConcurrent::run( &t,&Test::getCount, 4 );// 传入对象地址
QFutureWatcher<int> w;
w.setFuture (f);
QObject::connect (&w,&QFutureWatcher<int>::finished,[&]{
qDebug() << QThread::currentThread ();
qDebug()<< f.result () << f.resultCount () ;
a.quit (); // 主动退出,否则进程一直在循环
});
qDebug() << "end" << QThread::currentThread ();
return a.exec (); // 开启事件序列,等待信号槽的处理
}
代码结果和第5项一样,截图略。
总结
如果函数有返回值,并用 QFuture 查询了返回值,这个操作会导致阻塞线程。