在Qt中进行多线程编程,可以利用Qt提供的多种机制和类来简化并发任务的处理。Qt支持C++11及之后的标准中的std::thread,但更推荐使用其自身提供的一些高级抽象,如QThread、QThreadPool、QtConcurrent等,这些工具能够更好地与Qt的事件循环机制集成,并提供了更便捷的任务管理和同步方式。
1. Qt线程模型核心概念
1.1 主线程(GUI线程)
-
职责:处理UI事件(按钮点击、绘图等)。
-
规则:所有GUI操作必须在此线程执行。
1.2 工作线程
-
职责:执行耗时操作(文件I/O、计算等)。
-
通信方式:通过信号槽与主线程交互。
2. 多线程实现方式
2.1 继承 QThread(传统方式,不推荐)
QThread 是Qt中用于实现线程的主要类。可以通过继承QThread,并重写其run()方法来定义新线程的行为。
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class WorkerThread : public QThread {
void run() override {
qDebug() << "Worker thread started:" << QThread::currentThread();
// 执行一些耗时操作
msleep(500); // 模拟工作
qDebug() << "Worker thread finished:" << QThread::currentThread();
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
WorkerThread worker;
QObject::connect(&worker, &WorkerThread::finished, &a, &QCoreApplication::quit);
worker.start();
return a.exec();
}
2.2 使用 QObject + moveToThread(推荐方式)
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class Worker : public QObject {
Q_OBJECT
public slots:
void doWork() {
// 耗时操作(在子线程执行)
emit resultReady(result);
}
signals:
void resultReady(int result);
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
// 主线程设置
QThread* thread = new QThread;
Worker* worker = new Worker;
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &MainWindow::handleResult);
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();
return a.exec();
}
2.3 使用 QtConcurrent 和 QFuture
QtConcurrent 提供了一个高层次的接口来进行多线程编程,允许你以一种更加声明式的方式编写并发代码。它包括函数如run()、map()、filter()等,以及QFuture类用于获取异步计算的结果。
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrentRun>
#include <QFuture>
#include <QDebug>
int longRunningFunction() {
qDebug() << "Processing in thread:" << QThread::currentThread();
QThread::sleep(2); // 模拟长时间运行的任务
return 42;
}
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QFuture<int> future = QtConcurrent::run(longRunningFunction);
QObject::connect(&future, &QFutureWatcher<int>::finished, [&]() {
qDebug() << "Result:" << future.result();
a.quit();
});
return a.exec();
}
QFutureSynchronizer 是 Qt 中用于同步多个 QFuture 对象的便捷类。它允许你等待一组异步操作全部完成,而不需要单独管理每个 QFuture 的状态或结果。这对于需要同时启动多个异步任务,并在所有任务完成后执行某些操作的场景非常有用。
并行数据处理:例如,同时从多个数据库查询数据或处理多个文件时,可以在所有任务完成后合并结果。
GUI应用中的后台任务:当你有多个耗时操作需要在后台执行,并且希望在所有操作完成后更新用户界面
主要特点
- 自动管理多个 QFuture:可以添加任意数量的 QFuture 到 QFutureSynchronizer 中。
- 等待所有任务完成:通过 waitForFinished() 方法,可以阻塞当前线程直到所有关联的 QFuture 完成为止。
- 不会获取或存储结果:QFutureSynchronizer 仅用于同步目的,不负责存储或访问各个 QFuture 的结果。
使用示例
以下是一个简单的例子,演示了如何使用 QFutureSynchronizer 来同步两个异步计算任务:
#include <QtConcurrent/QtConcurrentRun>
#include <QFutureSynchronizer>
#include <QCoreApplication>
#include <QDebug>
int longRunningFunction(int input) {
qDebug() << "Processing in thread:" << QThread::currentThread();
QThread::sleep(2); // 模拟长时间运行的任务
return input * 2;
}
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
// 创建两个异步任务
QFuture<int> future1 = QtConcurrent::run(longRunningFunction, 10);
QFuture<int> future2 = QtConcurrent::run(longRunningFunction, 20);
// 创建 QFutureSynchronizer 并添加两个 QFuture
QFutureSynchronizer<int> synchronizer;
synchronizer.addFuture(future1);
synchronizer.addFuture(future2);
// 等待所有任务完成
synchronizer.waitForFinished();
// 获取结果
qDebug() << "Result of future1:" << future1.result();
qDebug() << "Result of future2:" << future2.result();
return app.exec();
}
filteredReduced 组合操作:过滤与归约
QList<int> numbers = {1, 2, 3, 4, 5, 6};
// 并行操作:筛选偶数并求和
QFuture<int> future = QtConcurrent::filteredReduced(
numbers,
[](int x) { return x % 2 == 0; }, // 过滤条件
[](int& sum, int x) { sum += x; } // 归约函数
);
future.waitForFinished();
qDebug() << "Sum of even numbers:" << future.result(); // 输出 12
QFutureWatcher 的监控
通过 QFutureWatcher 实现异步任务的事件驱动处理:
QFutureWatcher<QString> watcher;
// 连接信号
connect(&watcher, &QFutureWatcher<QString>::finished, [&]() {
qDebug() << "Task finished. Result:" << watcher.result();
});
connect(&watcher, &QFutureWatcher<QString>::progressRangeChanged,
[](int min, int max) { qDebug() << "Progress range:" << min << "-" << max; });
connect(&watcher, &QFutureWatcher<QString>::progressValueChanged,
[](int value) { qDebug() << "Progress:" << value; });
// 启动任务
QFuture<QString> future = QtConcurrent::run([]() {
QThread::sleep(1);
return "Done";
});
watcher.setFuture(future);
2.4 使用 QThreadPool 和 QRunnable
QThreadPool管理一组QRunnable对象的工作线程池。这有助于限制应用程序中同时运行的线程数量,从而避免资源过度消耗。
#include <QCoreApplication>
#include <QThreadPool>
#include <QRunnable>
#include <QDebug>
class Task : public QRunnable {
public:
void run() override {
qDebug() << "Task running in thread:" << QThread::currentThread();
// 执行任务...
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
Task *task = new Task();
task->setAutoDelete(true); // 自动删除任务对象
QThreadPool::globalInstance()->start(task);
return a.exec();
}
3. 线程间通信
3.1 信号槽机制
-
自动跨线程通信:若信号槽连接类型为
Qt::QueuedConnection(默认跨线程时自动启用),参数会被复制到目标线程的事件队列。
connect(worker, &Worker::dataUpdated, this, [this](Data data){
ui->label->setText(data.toString()); // 安全更新UI
}, Qt::QueuedConnection);
3.2 共享数据保护
-
使用
QMutex、QReadWriteLock:
QMutex mutex;
QList<Data> sharedList;
void addData(const Data& data) {
QMutexLocker locker(&mutex); // 自动加锁/解锁
sharedList.append(data);
}
4. 关键注意事项
.1 QObject线程亲和性
-
规则:QObject实例属于创建它的线程,不可跨线程直接访问。
-
安全操作:
- 使用
QMetaObject::invokeMethod:
QMetaObject::invokeMethod(worker, "updateData", Qt::QueuedConnection, Q_ARG(Data, data));
-
或通过信号槽传递数据。
4.2 资源释放
自动清理:使用 QObject::deleteLater 安全释放对象:
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
4.3 避免死锁
-
顺序:多个锁按固定顺序获取。
-
超时机制:使用
QMutex::tryLock(timeout)。
5. 总结
- 减少跨线程通信频率:批量传递数据而非频繁发送信号。
- 使用无锁结构:如 QAtomicInt 或第三方无锁容器。
- 线程池调优:通过 QThreadPool::setMaxThreadCount() 控制并发数。
- 主从分离:GUI操作在主线程,耗时操作在子线程。
- 通信规范:通过信号槽跨线程交互,避免直接调用。
- 资源管理:利用Qt的父子对象机制和 deleteLater 安全释放资源。
- 性能优先:合理使用线程池和异步API(如 QNetworkAccessManager)
6. 完整示例:文件哈希计算器
// FileHasher.h
class FileHasher : public QObject {
Q_OBJECT
public:
explicit FileHasher(QObject* parent = nullptr) : QObject(parent) {}
public slots:
void computeHash(const QString& filePath) {
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
emit error("Cannot open file");
return;
}
QCryptographicHash hash(QCryptographicHash::Sha256);
if (hash.addData(&file)) {
emit hashComputed(hash.result().toHex());
} else {
emit error("Failed to compute hash");
}
}
signals:
void hashComputed(const QByteArray& hash);
void error(const QString& message);
};
// MainWindow.cpp
void MainWindow::onHashButtonClicked() {
QString filePath = QFileDialog::getOpenFileName(this);
QThread* thread = new QThread;
FileHasher* hasher = new FileHasher;
hasher->moveToThread(thread);
connect(thread, &QThread::started, [=] { hasher->computeHash(filePath); });
connect(hasher, &FileHasher::hashComputed, this, [=](const QByteArray& hash) {
ui->resultLabel->setText(hash);
thread->quit();
});
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
connect(thread, &QThread::finished, hasher, &QObject::deleteLater);
thread->start();
}
2156

被折叠的 条评论
为什么被折叠?



