这是我像deepseek提问得到回答 。我本意是问qt和std::thread怎样配合达到不阻塞主线程的目的。

他给出qt和std::thread的配合方式,比较啰嗦。而qt和qthread的配合相对简洁。我就先将qt,qthread的配合方式贴上。后面有我的理解。

在 Qt 中,要实现不阻塞主线程(通常是UI线程)的并发任务,核心思想是绝不在主线程中进行阻塞式等待(如 waitForFinished()),而是通过信号与槽的机制,在任务完成后得到通知并处理结果。

结合你提到的 QThread 和 QFutureWatcher,主要有两种经典且安全的实现方式。下面我将分别进行介绍

方法一:使用 QThread + 信号/槽 (Worker对象模式)

这是最传统、最灵活的Qt多线程方式。核心是将耗时任务放在一个继承自 QObject 的工作类(Worker)中,然后将这个工作对象移动到一个 QThread 实例里执行。通过信号与槽进行跨线程通信,从而避免阻塞主线程。
实现步骤
创建Worker类:定义一个继承自 QObject 的类,在其中实现耗时的计算函数,并定义一个携带计算结果的信号。
创建并配置线程:在主线程中实例化 QThread 和 Worker 对象。
移动Worker到线程:调用 worker->moveToThread(&thread) 将工作对象移至新线程。
连接信号与槽:
将启动任务的信号(如按钮点击)连接到 Worker 的耗时槽函数。
将 Worker 的结果信号连接到主线程中处理结果的槽函数。
连接线程的启动和结束信号以管理生命周期。
启动线程:调用 thread.start()。
代码示例
#include <QCoreApplication>
#include <QCoreApplication>
#include <QThread>
#include <QObject>
#include <QDebug>
#include <QTimer>

// 1. 创建工作类 (Worker),继承自 QObject
class Worker : public QObject {
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr) : QObject(parent) {}

signals:
// 定义信号,用于向主线程发送结果
void resultReady(int result);
public slots:
// 定义槽函数,执行耗时任务
void doWork(int parameter) {
qDebug() << "工作线程开始,线程ID:" << QThread::currentThreadId();
// 模拟耗时计算
int result = 0;
for (int i = 0; i < parameter; ++i) {
QThread::msleep(10); // 模拟计算步骤
result += i;
}
qDebug() << "工作线程完成,线程ID:" << QThread::currentThreadId();
// 通过信号将结果发送回主线程
emit resultReady(result);
}
};

// 主线程中的使用示例
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
qDebug() << "主线程ID:" << QThread::currentThreadId();
// 2. 创建线程对象和工作对象
QThread workerThread;
Worker worker;

// 3. 将工作对象移动到工作线程
worker.moveToThread(&workerThread);

// 4. 建立连接
// 当工作线程启动后,开始执行耗时任务
QObject::connect(&workerThread, &QThread::started, &worker, & {
worker.doWork(100); // 可以传递参数
});
// 当工作对象发出结果信号时,在主线程中处理结果
QObject::connect(&worker, &Worker::resultReady, [&](int result) {
qDebug() << "主线程收到结果:" << result;
// 假设处理完结果后退出应用
QTimer::singleShot(1000, &app, &QCoreApplication::quit);
});
// 清理线程
QObject::connect(&worker, &Worker::resultReady, &workerThread, &QThread::quit);
QObject::connect(&workerThread, &QThread::finished, &worker, &Worker::deleteLater);

// 5. 启动线程
workerThread.start();

qDebug() << "主线程未被阻塞,继续执行...";
return app.exec();
}

include "main.moc" // 如果使用单独的 .cpp 文件,需要包含 moc 文件

为什么这种方式不阻塞主线程?
因为主线程调用了 thread.start() 后,会立刻返回并进入事件循环(app.exec())。耗时任务在另一个线程中执行,而主线程的事件循环会继续处理界面绘制、用户输入等事件。当工作线程完成后,通过 resultReady 信号通知主线程,主线程再在它的上下文中处理结果。

方法二:使用 QtConcurrent::run + QFutureWatcher (更简洁的现代方式)

如果你的任务是一个独立的函数,不需要在它的生命周期内与主线程频繁交互,那么 QtConcurrent 是更简洁高效的选择。QtConcurrent::run 会自动从全局线程池中获取一个线程来运行你的函数,并返回一个 QFuture 对象。配合 QFutureWatcher,你可以监控这个 QFuture 的状态,并在其完成时得到通知。
实现步骤
包含模块:在 .pro 文件中添加 QT += concurrent,并在头文件中包含 <QtConcurrent/QtConcurrent>。
编写耗时函数:定义一个普通函数或仿函数,它将在线程池中执行。
创建 QFutureWatcher:创建一个 QFutureWatcher 对象,其模板参数与你的函数返回值类型一致。
连接 finished 信号:将 QFutureWatcher::finished() 信号连接到处理结果的槽函数。
启动任务并开始监控:调用 QtConcurrent::run 启动异步任务,并将返回的 QFuture 对象通过 watcher.setFuture() 设置给监控器。
代码示例
#include
#include <QtConcurrent/QtConcurrent>
#include <QFutureWatcher>
#include <QDebug>
#include <QThread>
// 1. 一个耗时的普通函数
int heavyComputation(int input) {
qDebug() << "计算线程开始,线程ID:" << QThread::currentThreadId();
QThread::sleep(2); // 模拟耗时2秒
int result = input * input;
qDebug() << "计算线程完成,线程ID:" << QThread::currentThreadId();
return result;
}

int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
qDebug() << "主线程ID:" << QThread::currentThreadId();

// 2. 创建 QFutureWatcher 对象,用于监控结果
QFutureWatcher watcher;

// 3. 连接 finished 信号,当任务完成时在主线程中处理
QObject::connect(&watcher, &QFutureWatcher::finished, & {
int result = watcher.result(); // 获取结果,此时不会阻塞
qDebug() << "主线程收到计算结果:" << result;
QCoreApplication::quit(); // 任务完成,退出应用
});

// 4. 使用 QtConcurrent::run 启动异步任务,并开始监控
QFuture future = QtConcurrent::run(heavyComputation, 42);
watcher.setFuture(future);

qDebug() << "主线程未被阻塞,可以继续处理界面事件...";
return app.exec();
}

为什么这种方式更简单?
你无需手动管理 QThread 对象的生命周期、moveToThread 以及线程清理工作。QtConcurrent 自动处理了这一切。QFutureWatcher 的 finished 信号会自动在创建它的线程(通常是主线程)中发射,因此可以安全地在槽函数中更新UI。
特性 QThread + QObject (Worker) QtConcurrent::run + QFutureWatcher
核心思想 将对象移动到线程,通过信号/槽通信 将函数提交到线程池,通过 QFutureWatcher 监控
线程管理 手动创建、启动、清理 QThread 自动使用全局或指定的 QThreadPool
与主线程通信 通过自定义信号传递结果和中间状态 通过 finished、progressValueChanged 等预定义信号
适用场景 任务生命周期内需多次与主线程交互(如上报进度、请求数据) 一次性后台计算任务,任务完成后只需取一次结果
代码复杂度 稍高,需要编写 Worker 类和连接信号 较低,通常只需一个函数和一个 QFutureWatcher
总结
在Qt中实现非阻塞的并发任务,核心就是将耗时操作移出主线程,并通过异步的方式获取结果。
如果你需要更精细地控制线程的生命周期,或者需要在任务执行过程中与主线程进行多次交互(例如更新进度条),推荐使用 QThread + Worker 对象模式。
如果你的任务是独立的函数,只需要在结束时获取一次结果,那么 QtConcurrent::run + QFutureWatcher 是更简洁、更现代的选择。
这也是 Qt 官方文档中推荐用于创建非阻塞GUI应用的常用方法。

我的理解,其实可以将耗时函数放到 由QObject对象的一个槽函数中,当该槽函数执行完后像主线程法信号通知主线程继续运行。和第一中方法相似 例如改写 doWork槽函数

void doWork(int parameter) {
{
int n=5;
std::vectorstd::thread threads;
for(int i=0;i<5;++i)
{
std::thread th(function ,param1...);
threads.emplace_back(th);

}

for(auto &t:threads)
{
t.join();
}

emit sigFinished();//通知主线程计算完毕

}//end function

当然 doWork 函数必须通过信号启动才能保证该函数也在后台线程中运行。从而达到 和方法一的效果。``

posted on 2026-02-27 16:11  keleman  阅读(45)  评论(0)    收藏  举报