QtConcurrent::run并发
QtConcurrent::run:(在另一个线程中运行一个函数。)
相当于:
QtConcurrent::run(QThreadPool::globalInstance(), function, ...);
在单独的线程中运行function 。线程取自全局QThreadPool 。请注意,function 可能不会立即运行;function 只有在线程可用时才会运行。
在基本模式下,T 与function 的返回值类型相同。非虚返回值可通过QFuture::result() 函数访问。
在基本模式下,返回的QFuture 只能用于查询函数的运行/完成状态和返回值。特别是,只有在未来的计算尚未开始时,才能取消或暂停。
在带承诺运行模式下,function 将返回 void,并且必须接受一个QPromise<T> &
类型的额外参数,作为第一个参数放在函数参数列表中。T 是结果类型,与返回的QFuture<T>
相同。
在带承诺运行模式下,与基本模式类似,返回的QFuture 可用于查询函数的运行/完成状态和报告值。此外,它还可用于暂停或取消正在运行的任务,从被调用的function 获取多个结果,或监控function 报告的进度。
QtConcurrent::run() 函数在单独的线程中运行一个函数。函数的返回值通过QFuture API 提供。
QtConcurrent::run() 是一个重载方法。您可以将这些重载视为略有不同的模式。在基本模式下,传递给 QtConcurrent::run() 的函数只能向调用者报告一个计算结果。在 "带承诺运行 "模式下,传递给 QtConcurrent::run() 的函数可以使用附加的QPromise API,该 API 可实现多重结果报告、进度报告、在调用者要求时暂停计算或在调用者要求时停止计算。
该函数是Qt Concurrent 框架的一部分。Concurrent Run | Qt Concurrent 6.8.2
并发运行(基本模式)
传递给 QtConcurrent::run() 的函数可通过其返回值报告结果。
在独立线程中运行函数
要在另一个线程中运行函数,请使用 QtConcurrent::run():
extern void aFunction();
QFuture<void> future = QtConcurrent::run(aFunction);
这将在从默认QThreadPool 获取的独立线程中运行aFunction
。您可以使用QFuture 和QFutureWatcher 类监控函数的状态。
要使用专用线程池,可以将QThreadPool 作为第一个参数:
extern void aFunction();
QThreadPool pool;
QFuture<void> future = QtConcurrent::run(&pool, aFunction);
向函数传递参数
向函数传递参数的方法是将参数添加到 QtConcurrent::run() 调用中,紧跟在函数名称之后。例如
extern void aFunctionWithArguments(int arg1, double arg2, const QString &string);
int integer = ...;
double floatingPoint = ...;
QString string = ...;
QFuture<void> future = QtConcurrent::run(aFunctionWithArguments, integer, floatingPoint, string);
在调用 QtConcurrent::run() 时,会复制每个参数,这些值会在线程开始执行函数时传递给线程。调用 QtConcurrent::run() 后对参数所做的更改线程是看不到的。
请注意,QtConcurrent::run 不支持直接调用重载函数。例如,下面的代码将无法编译:
void foo(int arg);
void foo(int arg1, int arg2);
...
QFuture<void> future = QtConcurrent::run(foo, 42);
最简单的解决方法是通过 lambda 调用重载函数:
QFuture<void> future = QtConcurrent::run([] { foo(42); });
或者使用static_cast
告诉编译器选择哪个重载函数:
QFuture<void> future = QtConcurrent::run(static_cast<void(*)(int)>(foo), 42);
或qOverload :
QFuture<void> future = QtConcurrent::run(qOverload<int>(foo), 42);
从函数返回值
函数的任何返回值都可以通过QFuture 获取:
extern QString functionReturningAString();
QFuture<QString> future = QtConcurrent::run(functionReturningAString);
...
QString result = future.result();
如果不需要结果(例如,因为函数返回void
),使用QThreadPool::start() 重载获取函数对象会更有效。
如上文所述,传递参数是这样进行的:
extern QString someFunction(const QByteArray &input);
QByteArray bytearray = ...;
QFuture<QString> future = QtConcurrent::run(someFunction, bytearray);
...
QString result = future.result();
请注意,QFuture::result() 函数会阻塞并等待结果可用。当函数执行完毕、结果可用时,请使用QFutureWatcher 获取通知。
其他应用程序接口功能
使用成员函数
QtConcurrent::run() 也接受指向成员函数的指针。第一个参数必须是常量引用或指向类实例的指针。通过常量引用传递有助于调用常量成员函数;通过指针传递有助于调用修改实例的非常量成员函数。
例如,在一个单独的线程中调用QByteArray::split() (一个常量成员函数)是这样完成的:
// call 'QList<QByteArray> QByteArray::split(char sep) const' in a separate thread
QByteArray bytearray = "hello world";
QFuture<QList<QByteArray> > future = QtConcurrent::run(&QByteArray::split, bytearray, ' ');
...
QList<QByteArray> result = future.result();
调用非 const 成员函数的方法如下
// call 'void QImage::invertPixels(InvertMode mode)' in a separate thread
QImage image = ...;
QFuture<void> future = QtConcurrent::run(&QImage::invertPixels, &image, QImage::InvertRgba);
...
future.waitForFinished();
// At this point, the pixels in 'image' have been inverted
使用 Lambda 函数
调用 lambda 函数的方法如下
QFuture<void> future = QtConcurrent::run([=]() {
// Code in this block will run in another thread
});
...
调用函数修改通过引用传递的对象的过程如下:
static void addOne(int &n) { ++n; }
...
int n = 42;
QtConcurrent::run(&addOne, std::ref(n)).waitForFinished(); // n == 43
使用可调用对象的方法如下
struct TestClass
{
void operator()(int s1) { s = s1; }
int s = 42;
};
...
TestClass o;
// Modify original object
QtConcurrent::run(std::ref(o), 15).waitForFinished(); // o.s == 15
// Modify a copy of the original object
QtConcurrent::run(o, 42).waitForFinished(); // o.s == 15
// Use a temporary object
QtConcurrent::run(TestClass(), 42).waitForFinished();
// Ill-formed
QtConcurrent::run(&o, 42).waitForFinished(); // compilation error
使用承诺并发运行
与 QtConcurrent::run() 的基本模式相比,Run With Promise模式可对运行任务进行更多控制。它允许报告运行任务的进度、报告多个结果、根据请求暂停执行或根据调用者的要求取消任务。
强制 QPromise 参数
在Run With Promise(带承诺运行)模式下,传递给 QtConcurrent::run() 的函数应该有一个QPromise<T> &
类型的附加参数,其中T
是计算结果的类型(它应该与 QtConcurrent::run() 返回的QFuture<T> 的类型T
相匹配),例如:
extern void aFunction(QPromise<void> &promise);
QFuture<void> future = QtConcurrent::run(aFunction);
promise
参数在 QtConcurrent::run() 函数中被实例化,其引用被传递给调用的aFunction
,因此用户在此模式下调用 QtConcurrent::run() 时无需实例化它,也无需明确传递它。
QPromise 类型的附加参数始终需要作为第一个参数出现在函数的参数列表中,例如
extern void aFunction(QPromise<void> &promise, int arg1, const QString &arg2);
int integer = ...;
QString string = ...;
QFuture<void> future = QtConcurrent::run(aFunction, integer, string);
报告结果
与 QtConcurrent::run() 的基本模式不同,在Run With Promise模式下传递给 QtConcurrent::run() 的函数总是返回 void 类型。结果报告通过QPromise 类型的附加参数完成。它还支持多重结果报告,例如:
void helloWorldFunction(QPromise<QString> &promise)
{
promise.addResult("Hello");
promise.addResult("world");
}
QFuture<QString> future = QtConcurrent::run(helloWorldFunction);
...
QList<QString> results = future.results();
注意: 无需调用QPromise::start() 和QPromise::finish() 来指示计算的开始和结束(通常使用QPromise 时会这样做)。QtConcurrent::run() 总是会在开始和结束执行之前调用它们。
暂停和取消执行
如果需要,QPromise API 还可以暂停和取消计算:
void aFunction(QPromise<int> &promise)
{
for (int i = 0; i < 100; ++i) {
promise.suspendIfRequested();
if (promise.isCanceled())
return;
// computes the next result, may be time consuming like 1 second
const int res = ... ;
promise.addResult(res);
}
}
QFuture<int> future = QtConcurrent::run(aFunction);
... // user pressed a pause button after 10 seconds
future.suspend();
... // user pressed a resume button after 10 seconds
future.resume();
... // user pressed a cancel button after 10 seconds
future.cancel();
调用future.suspend()
时,运行中的任务会请求暂停执行。调用该方法后,运行任务将在其迭代循环中下一次调用promise.suspendIfRequested()
后暂停。在这种情况下,运行中的任务将阻塞对promise.suspendIfRequested()
的调用。被阻塞的调用将在调用future.resume()
后解除阻塞。请注意,suspendIfRequested() 内部使用等待条件来解除阻塞,因此运行中的线程会进入空闲状态,而不是在阻塞时浪费资源,以便定期检查恢复请求是否来自调用者的线程。
最后一行对future.cancel()
的调用导致下一次对promise.isCanceled()
的调用将返回true
,而aFunction
将立即返回,不再报告任何结果。
注意: 取消后无需调用QPromise::finish() 来停止计算(通常使用QPromise 时会这样做)。QtConcurrent::run() 总是会在执行结束后调用它。
进度报告
也可以独立于结果报告来报告任务的进度,例如
void aFunction(QPromise<int> &promise)
{
promise.setProgressRange(0, 100);
int result = 0;
for (int i = 0; i < 100; ++i) {
// computes some part of the task
const int part = ... ;
result += part;
promise.setProgressValue(i);
}
promise.addResult(result);
}
QFutureWatcher<int> watcher;
QObject::connect(&watcher, &QFutureWatcher::progressValueChanged, [](int progress){
... ; // update GUI with a progress
qDebug() << "current progress:" << progress;
});
// 连接信号以便知道异步任务何时完成
QObject::connect(watcher, &QFutureWatcher<int>::finished, this, [this, watcher](void) {
// 异步任务完成时的回调
auto info = watcher->result();
//todo
delete watcher;
});
watcher.setFuture(QtConcurrent::run(aFunction));
调用者为 QtConcurrent::run() 返回的QFuture
安装QFutureWatcher
,以便连接到progressValueChanged()
信号,并相应地更新图形用户界面等。
使用重载操作符()()调用函数
默认情况下,QtConcurrent::run() 在"带承诺运行"模式下不支持带有重载操作符()()的函数。在重载函数的情况下,用户需要明确指定结果类型作为模板参数传递给 QtConcurrent::run(),例如
struct Functor {
void operator()(QPromise<int> &) { }
void operator()(QPromise<double> &) { }
};
Functor f;
run<double>(f); // this will select the 2nd overload
// run(f); // error, both candidate overloads potentially match
QtConcurrent Namespace | Qt Concurrent 6.8.2
Concurrent Run | Qt Concurrent 6.8.2