news 2026/8/1 17:13:49

Qt程序设计:QPromise/QFuture多线程任务管理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt程序设计:QPromise/QFuture多线程任务管理

Qt6 在 QFutureInterface 基础上进一步封装出了 QPromise 类,搭配 QFuture 使用,逻辑类似 std::promise + std::future,线程内使用 promise 设置进度和结果,外部通过关联的 future 判断状态和获取结果。

#include <future> std::promise<T> p; // 获取与 promise 关联的 future std::future<T> f = p.get_future(); // 线程 A 中设置值 p.set_value(value); // 线程 B 中阻塞获取 promise 设置的值 T result = f.get();

使用 QFuture 的优势在于,搭配 QFutureWatcher 使用时可以获取 started / finished 等相关信号,这是 QFutureInterface 内部生成的事件被 QFutureWatcher 接收后发出的。

当需要后台执行任务时,界面上一般会有相应的进度提示,借助 QPromise / QFuture 相关接口可以很容易的实现这个功能。在此基础上做一点小小的封装,就得到了一个简易的任务管理类。

封装时一个麻烦的地方是,QFutureWatcher 需要在关联 QFuture 之前关联信号槽,这样我们的 run / start 开始任务接口就需要把所有的槽和槽接收者作为参数。不过这样写起来有点麻烦,就干脆拆成了两部分,先生成 QFutureWatcher,关联好信号槽后再开始任务。

另一个问题是 QPromise / QFuture 的模板类型参数我使用了 QObject*(懒得自定义基类),主要还是考虑到后续进行 QML 版封装用模板不方便,子类化任务返回类型实现起来更顺手一点。

后续扩展:如果要在 QML 中使用,任务接口和返回的 future 对象就需要封装。

TaskManager 实现代码:

#pragma once #include <QThreadPool> #include <QFuture> #include <QFutureWatcher> #include <QPromise> #include <functional> #include <list> // 任务管理 class TaskManager : public QObject { Q_OBJECT Q_DISABLE_COPY_MOVE(TaskManager) private: explicit TaskManager(QObject *parent = nullptr); public: ~TaskManager(); static TaskManager *instance(); // 任务结果暂时为QObject*,可以用一个自定义基类,包含type判断类型 using TPromise = QPromise<QObject*>; using TWatcher = QFutureWatcher<QObject*>; // 创建任务future watcher // 如果将槽函数作为run参数传入,代码有点乱,拆分为两步,先创建watcher关联信号槽,再run任务 auto create() -> std::shared_ptr<TWatcher>; // 执行任务,繁忙时任务排队 void run(const std::function<void (TPromise &)> &task, std::shared_ptr<TWatcher> watcher); // 判断是否繁忙 bool busy() const; // 取消所有任务 void cancel(); // 等待全部任务结束 void wait(); signals: void taskStarted(); void taskFinished(); private: // 任务线程池 QThreadPool taskPool; // 内部管理任务列表 std::list<std::shared_ptr<TWatcher>> taskList; // 任务列表锁 std::mutex listMutex; }; TaskManager::TaskManager(QObject *parent) : QObject{parent} { // 线程池最大线程数 taskPool.setMaxThreadCount(QThread::idealThreadCount()); // 线程空闲时,销毁判定时间,单位ms,默认半分钟 taskPool.setExpiryTimeout(1000 * 60); } TaskManager::~TaskManager() { cancel(); wait(); } TaskManager *TaskManager::instance() { static TaskManager manager; return &manager; } auto TaskManager::create() -> std::shared_ptr<TWatcher> { return std::make_shared<TWatcher>(); } void TaskManager::run(const std::function<void (TPromise &)> &task, std::shared_ptr<TWatcher> watcher) { if (!task || !watcher) return; TPromise promise{}; auto future = promise.future(); watcher->setFuture(future); { // 添加到任务列表 std::lock_guard<std::mutex> guard(listMutex); Q_UNUSED(guard) taskList.push_back(watcher); // qDebug() << "push task"; } // 丢进线程池泡澡 taskPool.start([this, task, watcher, promise = std::move(promise)]() mutable { promise.start(); task(promise); promise.finish(); { // 任务结束从列表移除 std::lock_guard<std::mutex> guard(listMutex); Q_UNUSED(guard) taskList.remove(watcher); // qDebug() << "remove task"; } }); } bool TaskManager::busy() const { // 活跃线程数达到上限 return (taskPool.activeThreadCount() >= taskPool.maxThreadCount()); } void TaskManager::cancel() { // 移除未启动的任务 taskPool.clear(); // 取消所有任务 std::lock_guard<std::mutex> guard(listMutex); Q_UNUSED(guard) for (auto &handle : taskList) { handle->cancel(); } } void TaskManager::wait() { // 等待最后一个线程开始 taskPool.waitForDone(-1); }

测试代码:

void MainWindow::onRun() { // 先创建future watcher关联信号槽 auto pwatcher = TaskManager::instance()->create(); auto watcher = pwatcher.get(); // 列表更新 auto table = ui->tableWidget; int row = table->rowCount(); table->insertRow(row); QString time = QTime::currentTime().toString("hh:mm:ss.zzz"); auto title = new QLabel(time); table->setCellWidget(row, 0, title); auto bar = new QProgressBar(); table->setCellWidget(row, 1, bar); auto btn = new QPushButton("Cancel"); table->setCellWidget(row, 2, btn); connect(btn, &QPushButton::clicked, [pwatcher]{ pwatcher->cancel(); }); // 测试对象释放 connect(btn, &QPushButton::destroyed, this, [time]{ qDebug() << "free" << time; }); // 状态更新 connect(watcher, &TaskManager::TWatcher::progressTextChanged, this, [](const QString &text){ // qDebug() << "progress text changed" << text; }); connect(watcher, &TaskManager::TWatcher::progressRangeChanged, this, [this, bar](int min, int max){ // qDebug() << "progress range changed" << min << max; bar->setRange(min, max); bar->setValue(min); }); connect(watcher, &TaskManager::TWatcher::progressValueChanged, this, [this, bar](int value){ // qDebug() << "progress changed" << value; bar->setValue(value); }); connect(watcher, &TaskManager::TWatcher::started, this, [time](){ qDebug() << "started" << time; }); connect(watcher, &TaskManager::TWatcher::finished, this, [this, table, time, btn, pwatcher](){ qDebug() << "finished" << time; int index = -1; for (int row = 0; row < table->rowCount(); ++row) { QPushButton *item = static_cast<QPushButton *>(table->cellWidget(row, 2)); if (item && item == btn) { index = row; break; } } if (index < 0) return; ui->tableWidget->removeRow(index); }); connect(watcher, &TaskManager::TWatcher::canceled, this, [this, time](){ qDebug() << "canceled" << time; }); connect(watcher, &TaskManager::TWatcher::resultReadyAt, this, [this, time, pwatcher](int index){ qDebug() << "result" << time << index << pwatcher->resultAt(index); }); // 执行任务,并通过关联的future watcher更新ui auto task = [](TaskManager::TPromise &promise){ promise.setProgressRange(0, 10); for (int i = 0; i < 10 && !promise.isCanceled(); ++i) { promise.setProgressValue(i); QThread::sleep(1); } if (promise.isCanceled()) return; promise.setProgressValue(10); promise.addResult(nullptr); }; TaskManager::instance()->run(task, pwatcher); } void MainWindow::onCancel() { TaskManager::instance()->cancel(); }

完整代码:https://github.com/gongjianbo/MyTestCode/tree/master/Qt6/TestQt_20251220_Future

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/1 15:27:10

18、Windows 10:通信、日程与照片管理全攻略

Windows 10:通信、日程与照片管理全攻略 1. 电子邮件管理 1.1 添加邮箱账户 Windows 10 让用户轻松添加和切换邮箱账户,将所有邮件集中在一处。默认情况下,系统可能已设置好与微软账户关联的邮箱,但用户还能添加其他邮箱,如 Gmail、Yahoo! Mail 等。具体操作步骤如下:…

作者头像 李华
网站建设 2026/8/1 12:26:37

Linly-Talker支持语音跨句连贯性

Linly-Talker 支持语音跨句连贯性 在虚拟主播深夜直播带货的场景中&#xff0c;观众最怕什么&#xff1f;不是产品不好&#xff0c;而是那个“数字人”像机器人一样&#xff0c;每说一句话就卡顿半秒&#xff0c;语气生硬、语调重置、呼吸全无——明明是连续讲解&#xff0c;听…

作者头像 李华
网站建设 2026/7/30 0:13:12

21、Windows 10系统维护与优化全攻略

Windows 10系统维护与优化全攻略 在使用Windows 10系统的过程中,对系统进行合理的维护和优化能够提升使用体验、保障数据安全。下面将为大家详细介绍文件备份与恢复、故障排查、硬盘优化以及设备加密等方面的操作方法。 文件备份与恢复 文件备份是保障数据安全的重要手段。…

作者头像 李华
网站建设 2026/7/31 23:22:56

Linly-Talker实测报告:延迟、清晰度、自然度全面达标

Linly-Talker实测报告&#xff1a;延迟、清晰度、自然度全面达标 在虚拟主播直播间里&#xff0c;一个由AI驱动的数字人正用标准普通话讲解最新科技动态——口型精准同步&#xff0c;语调自然起伏&#xff0c;甚至连微笑和眨眼都恰到好处。这不是电影特效&#xff0c;而是基于 …

作者头像 李华
网站建设 2026/8/1 23:36:39

4、Windows 10基础操作与文件管理全解析

Windows 10基础操作与文件管理全解析 1. Windows 10对话框操作 在Windows 10中,与典型对话框进行交互是常见的操作。以下是具体步骤: 1. 打开打印对话框 :点击功能区上的“文件”选项卡,然后点击“打印”以打开“打印”对话框。 2. 选择打印页面 :点击“页面”选项…

作者头像 李华
网站建设 2026/8/1 19:56:57

13、个性化Windows环境全攻略

个性化Windows环境全攻略 在使用Windows操作系统时,个性化设置能让你的电脑更符合个人喜好和工作需求。下面将详细介绍如何对Windows桌面、锁屏、屏幕保护程序等进行个性化设置,还会涉及主题的选择与保存。 1. 更改桌面颜色 除了更换桌面背景,还可以利用“个性化”窗口中的…

作者头像 李华