并发任务
QtConcurrent::task提供了在单独线程中运行任务的另一种接口。函数的返回值通过QFuture API提供。
如果您只想在单独的线程中运行一个函数而不调整任何参数,则使用QtConcurrent::run,因为它允许您编写更少的代码。QtConcurrent::task是为了那些需要执行额外配置步骤的情况而设计的。
此函数是Qt 并发框架的一部分。
流畅接口
QtConcurrent::task返回一个名为QtConcurrent::QTaskBuilder的辅助类实例。通常,您不需要手动创建此类实例。QtConcurrent::QTaskBuilder提供了一种通过链式方式调整不同任务参数的接口。这种方法被称为流畅接口。
您只需设置所需的参数,然后启动一个任务。为了最终确定任务的配置,您必须调用QtConcurrent::QTaskBuilder::spawn。此函数是非阻塞的(即立即返回一个将来对象),但保证任务不会立即启动。您可以使用QFuture和QFutureWatcher类来监控任务的状态。
下面有更多示例和说明。
在单独的线程中运行任务
要在线程中运行一个函数,请使用QtConcurrent::QTaskBuilder::spawn
QtConcurrent::task([]{ qDebug("Hello, world!"); }).spawn();
这将运行一个在默认的QThreadPool中获得的单独线程的lambda函数。
传递参数到任务
通过将它们传递给QtConcurrent::QTaskBuilder::withArguments来调用带有参数的函数
auto task = [](const QString &s){ qDebug() << ("Hello, " + s); }; QtConcurrent::task(std::move(task)) .withArguments("world!") .spawn();
QtConcurrent::QTaskBuilder::withArguments被调用时,将制作每个参数的副本,并且当线程开始执行任务时传递这些值。调用QtConcurrent::QTaskBuilder::withArguments之后的参数更改对线程不可见。
如果您想运行接受引用参数的函数,应使用std::ref/cref辅助函数。这些函数在传递参数的周围创建薄包装器
QString s("Hello, "); QtConcurrent::task([](QString &s){ s.append("world!"); }) .withArguments(std::ref(s)) .spawn();
请确保所有包装的持久性足够。如果跳出包装器后任务仍然存在,则可能会出现未定义的行为。
从任务中返回值
您可以使用 QFuture API 获取任务的执行结果
auto future = QtConcurrent::task([]{ return 42; }).spawn(); auto result = future.result(); // result == 42
请注意,QFuture::result() 是一个阻塞调用,它等待结果可用。使用 QFutureWatcher 在任务执行完成且结果可用时获取通知。
如果您想要将结果传递给另一个异步任务,可以使用 QFuture::then() 来创建一个依赖任务的链。有关详细信息,请参阅 QFuture 文档。
其他 API 特性
使用不同类型的可调用对象
严格来说,可以使用满足以下条件的任何类型任务和参数
std::is_invocable_v<std::decay_t<Task>, std::decay_t<Args>...>
您可以使用一个自由函数
QVariant value(42); auto result = QtConcurrent::task([](const QVariant &var){return qvariant_cast<int>(var);}) .withArguments(value) .spawn() .result(); // result == 42
您可以使用一个成员函数
QString result("Hello, world!"); QtConcurrent::task(&QString::chop) .withArguments(&result, 8) .spawn() .waitForFinished(); // result == "Hello"
您可以使用一个具有 operator() 的可调用对象
auto result = QtConcurrent::task(std::plus<int>()) .withArguments(40, 2) .spawn() .result() // result == 42
如果您想使用现有的可调用对象,您需要将其复制/移动到 QtConcurrent::task 或使用 std::ref/cref 进行包装
struct CallableWithState { void operator()(int newState) { state = newState; } // ... }; // ... CallableWithState object; QtConcurrent::task(std::ref(object)) .withArguments(42) .spawn() .waitForFinished(); // The object's state is set to 42
使用自定义线程池
您可以指定一个自定义线程池
QThreadPool pool; QtConcurrent::task([]{ return 42; }).onThreadPool(pool).spawn();
为任务设置优先级
您可以设置任务的优先级
QtConcurrent::task([]{ return 42; }).withPriority(10).spawn();
如果您不需要未来对象,可以调用 QtConcurrent::QTaskBuilder::spawn(QtConcurrent::FutureResult::Ignore)
QtConcurrent::task([]{ qDebug("Hello, world!"); }).spawn(FutureResult::Ignore);
您可以通过在函数内定义一个额外的具有 QPromise<T> &
类型参数的参数来访问与任务关联的承诺对象。此额外参数必须传递给函数的第一个参数,并且类似于在 Concurrent Run With Promise 模式下,该函数预期返回 void 类型。结果报告通过 QPromise API 完成
void increment(QPromise<int> &promise, int i) { promise.addResult(i + 1); } int result = QtConcurrent::task(&increment).withArguments(10).spawn().result(); // result == 11
© 2024 Qt 公司有限公司。此处包含的文档贡献者是各自所有者的版权。此处提供的文档根据 GNU 自由文档许可证版本 1.3 的条款发布,该许可证由自由软件基金会发布。Qt 及其相应标志是芬兰以及/或其他国家的 Qt 公司的商标。所有其他商标均为各自所有者的财产。