Qt多线程编程

在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 共享数据保护
  • 使用 QMutexQReadWriteLock

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. 总结
  1. 减少跨线程通信频率:批量传递数据而非频繁发送信号。
  2. 使用无锁结构:如 QAtomicInt 或第三方无锁容器。
  3. 线程池调优:通过 QThreadPool::setMaxThreadCount() 控制并发数。
  4. 主从分离:GUI操作在主线程,耗时操作在子线程。
  5. 通信规范:通过信号槽跨线程交互,避免直接调用。
  6. 资源管理:利用Qt的父子对象机制和 deleteLater 安全释放资源。
  7. 性能优先:合理使用线程池和异步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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值