QT异步线程通信

在使用 QThreadPool 提交任务后,如果你需要知道任务何时完成,并且需要使用任务的执行结果,可以通过以下几种方式来实现:

1. 使用信号和槽

QRunnable 提供了一个 finished() 信号,当任务执行完成后会发出。你可以在任务完成后通过信号和槽机制通知主线程。

示例代码
#include <QCoreApplication>
#include <QThreadPool>
#include <QRunnable>
#include <QDebug>
#include <QThread>

class Worker : public QRunnable {
public:
    Worker() {
        // 连接 finished 信号到自定义槽
        connect(this, &Worker::finished, this, &Worker::onFinished);
    }

    void run() override {
        qDebug() << "Worker running in thread" << QThread::currentThreadId();
        // 模拟耗时任务
        QThread::sleep(3);
        qDebug() << "Worker finished";
        emit finished(); // 发出任务完成信号
    }

private slots:
    void onFinished() {
        qDebug() << "Worker finished in thread" << QThread::currentThreadId();
        // 在这里可以处理任务完成后的逻辑
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    // 获取全局线程池
    QThreadPool* globalThreadPool = QThreadPool::globalInstance();

    // 创建一个任务
    Worker* worker = new Worker();

    // 将任务添加到全局线程池
    qDebug() << "Starting worker in main thread" << QThread::currentThreadId();
    globalThreadPool->start(worker);

    // 主线程继续运行
    qDebug() << "Main thread continues running immediately";
    QThread::sleep(1); // 等待一段时间,观察输出
    qDebug() << "Main thread still running";

    return app.exec();
}

输出示例

Starting worker in main thread 0x1234
Main thread continues running immediately
Main thread still running
Worker running in thread 0x5678
Worker finished
Worker finished in thread 0x5678

2. 使用 QFutureQtConcurrent::run

如果你需要更灵活的异步任务管理,可以使用 QtConcurrent::run,它会返回一个 QFuture 对象,你可以通过它来检查任务的状态或获取任务的返回值。

示例代码
#include <QCoreApplication>
#include <QtConcurrent>
#include <QDebug>
#include <QThread>
#include <QFuture>
#include <QFutureWatcher>

int workerFunction() {
    qDebug() << "Worker running in thread" << QThread::currentThreadId();
    // 模拟耗时任务
    QThread::sleep(3);
    qDebug() << "Worker finished";
    return 42; // 返回结果
}

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    // 使用 QtConcurrent::run 提交任务
    QFuture<int> future = QtConcurrent::run(workerFunction);

    // 主线程继续运行
    qDebug() << "Main thread continues running immediately";
    QThread::sleep(1); // 等待一段时间,观察输出
    qDebug() << "Main thread still running";

    // 等待任务完成并获取结果
    qDebug() << "Waiting for worker to finish...";
    int result = future.result(); // 阻塞主线程,直到任务完成
    qDebug() << "Worker result:" << result;

    return app.exec();
}

输出示例

Main thread continues running immediately
Main thread still running
Worker running in thread 0x5678
Worker finished
Waiting for worker to finish...
Worker result: 42

3. 使用 QThreadPool::waitForDone()

如果你需要等待所有任务完成,可以使用 QThreadPool::waitForDone() 方法。这个方法会阻塞当前线程,直到线程池中的所有任务都完成。

示例代码
#include <QCoreApplication>
#include <QThreadPool>
#include <QRunnable>
#include <QDebug>
#include <QThread>

class Worker : public QRunnable {
public:
    void run() override {
        qDebug() << "Worker running in thread" << QThread::currentThreadId();
        // 模拟耗时任务
        QThread::sleep(3);
        qDebug() << "Worker finished";
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    // 获取全局线程池
    QThreadPool* globalThreadPool = QThreadPool::globalInstance();

    // 创建一个任务
    Worker* worker = new Worker();

    // 将任务添加到全局线程池
    qDebug() << "Starting worker in main thread" << QThread::currentThreadId();
    globalThreadPool->start(worker);

    // 主线程继续运行
    qDebug() << "Main thread continues running immediately";
    QThread::sleep(1); // 等待一段时间,观察输出
    qDebug() << "Main thread still running";

    // 等待所有任务完成
    qDebug() << "Waiting for all tasks to finish...";
    globalThreadPool->waitForDone();
    qDebug() << "All tasks finished";

    return app.exec();
}

输出示例

Starting worker in main thread 0x1234
Main thread continues running immediately
Main thread still running
Worker running in thread 0x5678
Worker finished
Waiting for all tasks to finish...
All tasks finished

总结

  • 信号和槽:通过 QRunnable::finished 信号通知任务完成。
  • QFutureQtConcurrent::run:提供更灵活的异步任务管理,可以获取任务的返回值。
  • QThreadPool::waitForDone:等待线程池中的所有任务完成,但会阻塞当前线程。

根据你的需求选择合适的方法。如果你需要任务的返回值,建议使用 QtConcurrent::runQFuture


QtConcurrent::run 和直接使用 QThread 在功能和使用方式上有显著的区别。以下是它们的主要区别:

1. 使用方式

  • QtConcurrent::run

    • 更简单QtConcurrent::run 是一个高级接口,用于简化异步任务的提交和管理。它返回一个 QFuture 对象,可以用来检查任务的状态或获取任务的返回值。
    • 无需手动管理线程:你只需要提供一个函数或 lambda 表达式,QtConcurrent::run 会自动将任务提交到线程池中执行,无需手动创建和管理 QThread
    • 支持返回值QFuture 可以存储任务的返回值,方便在任务完成后获取结果。
  • QThread

    • 更灵活QThread 是一个低级接口,提供了对线程的细粒度控制。你可以创建自己的线程类,管理线程的启动、停止和同步。
    • 需要手动管理线程:你需要手动创建线程,连接信号和槽,管理线程的生命周期。
    • 不直接支持返回值:线程的执行结果需要通过信号和槽或其他机制传递回主线程。

2. 线程管理

  • QtConcurrent::run

    • 使用线程池QtConcurrent::run 内部使用 QThreadPool 来管理线程。任务会被提交到全局线程池中,由线程池负责分配线程。这种方式可以减少线程创建和销毁的开销,提高性能。
    • 自动管理线程生命周期:任务完成后,线程会自动返回线程池,无需手动管理。
  • QThread

    • 手动管理线程:你需要手动创建和启动线程,并在任务完成后手动停止线程。
    • 线程生命周期:线程的生命周期由你控制,需要确保线程在任务完成后正确退出。

3. 任务状态和结果

  • QtConcurrent::run

    • QFuture 提供状态检查QFuture 提供了多种方法来检查任务的状态,例如:
      • isFinished():检查任务是否完成。
      • isRunning():检查任务是否正在运行。
      • result():获取任务的返回值。
    • 支持异步操作QFuture 可以与 QFutureWatcher 配合使用,通过信号和槽机制在任务完成时通知主线程。
  • QThread

    • 手动检查状态:你需要通过信号和槽机制或手动检查线程的状态。
    • 不直接支持返回值:线程的执行结果需要通过信号和槽或其他机制传递回主线程。

4. 适用场景

  • QtConcurrent::run

    • 简单任务:适用于简单的异步任务,特别是那些不需要复杂线程管理的场景。
    • 任务结果处理:当你需要获取任务的返回值时,QFuture 提供了方便的接口。
  • QThread

    • 复杂任务:适用于需要更细粒度控制线程的复杂任务。
    • 长时间运行的任务:适用于需要长时间运行的后台任务,例如网络通信、文件处理等。

示例对比

使用 QtConcurrent::run
#include <QCoreApplication>
#include <QtConcurrent>
#include <QDebug>
#include <QThread>

int workerFunction() {
    qDebug() << "Worker running in thread" << QThread::currentThreadId();
    QThread::sleep(3);
    qDebug() << "Worker finished";
    return 42; // 返回结果
}

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    // 提交任务
    QFuture<int> future = QtConcurrent::run(workerFunction);

    // 主线程继续运行
    qDebug() << "Main thread continues running immediately";
    QThread::sleep(1);

    // 等待任务完成并获取结果
    qDebug() << "Waiting for worker to finish...";
    int result = future.result();
    qDebug() << "Worker result:" << result;

    return app.exec();
}
使用 QThread
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QMutex>
#include <QWaitCondition>

class Worker : public QObject {
    Q_OBJECT
public:
    Worker() : result(0), finished(false) {}

    void run() {
        qDebug() << "Worker running in thread" << QThread::currentThreadId();
        QThread::sleep(3);
        result = 42; // 设置结果
        finished = true; // 标记任务完成
        condition.wakeOne(); // 通知主线程
    }

    int getResult() const { return result; }
    bool isFinished() const { return finished; }

signals:
    void finished();

private:
    mutable QMutex mutex;
    QWaitCondition condition;
    int result;
    bool finished;
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    Worker worker;
    QThread thread;

    worker.moveToThread(&thread);

    QObject::connect(&thread, &QThread::started, &worker, &Worker::run);

    thread.start();

    qDebug() << "Main thread continues running immediately";
    QThread::sleep(1);

    // 等待任务完成
    qDebug() << "Waiting for worker to finish...";
    QMutexLocker locker(&worker.mutex);
    while (!worker.isFinished()) {
        worker.condition.wait(&locker);
    }
    int result = worker.getResult();
    qDebug() << "Worker result:" << result;

    thread.quit();
    thread.wait();

    return app.exec();
}

总结

  • QtConcurrent::run

    • 优点:简单易用,自动管理线程,支持返回值。
    • 缺点:功能相对有限,适合简单任务。
    • 适用场景:适合简单的异步任务,特别是需要获取任务结果的场景。
  • QThread

    • 优点:功能强大,支持复杂的线程管理。
    • 缺点:使用复杂,需要手动管理线程。
    • 适用场景:适合需要细粒度控制线程的复杂任务。

根据你的需求选择合适的方式。如果你的任务简单且需要返回值,推荐使用 QtConcurrent::run。如果你的任务复杂且需要更细粒度的线程管理,推荐使用 QThread


在 Qt 中,QFuture 是一个线程安全的对象,用于表示异步操作的结果。当你将 QFuture 作为值传递给其他函数时,实际上传递的是一个轻量级的“未来”对象的副本。这个副本与原始的 QFuture 对象共享底层的异步操作状态。

关键点

  • 共享状态:尽管 QFuture 是按值传递的,但它内部维护的是一个共享的状态。这意味着,无论你传递了多少个副本,它们都会指向同一个底层的异步操作状态。
  • 线程安全QFuture 的状态是线程安全的,因此你可以在多个线程中安全地访问和修改它的状态。

示例

假设你有以下代码:

void MachineFileBrowser::handleSingleClick(const QModelIndex &index)
{
    if (index.isValid() && lastClickedIndex == index)
    {
        QString path = model->getFilePath(index);
        fileloadFuture = QtConcurrent::run([this, path]() {
            MachineTree &tempTree = currentTemplate->getMachineTree();
            tempTree = MachineTree::parseFromTarXmlFile(path);  // 解析机器树数据
        });
        emit itemReClicked(index, fileloadFuture);
    } else
    {
        lastClickedIndex = index;
    }
}

itemReClicked 信号的槽函数中,你可以这样处理:

void SomeClass::handleItemReClicked(const QModelIndex &index, QFuture<void> future)
{
    // 检查任务是否完成
    if (future.isFinished())
    {
        qDebug() << "Task is finished";
    }
    else
    {
        qDebug() << "Task is still running";
    }
}

关键点解释

  1. fileloadFuture 的状态

    • fileloadFuture 是一个 QFuture<void> 对象,它表示一个异步操作的未来结果。
    • 当你将 fileloadFuture 传递给 itemReClicked 信号时,传递的是一个副本,但这个副本与原始的 fileloadFuture 共享底层的状态。
  2. 状态同步

    • 无论你在哪个地方访问 fileloadFuture,它的状态(例如 isFinished())始终是同步的。这是因为 QFuture 内部使用了共享的状态机制。
    • 这意味着,即使你在多个地方持有 fileloadFuture 的副本,它们的状态始终是一致的。

示例代码

假设你有一个槽函数 handleItemReClicked,它接收 fileloadFuture 的副本:

void SomeClass::handleItemReClicked(const QModelIndex &index, QFuture<void> future)
{
    // 检查任务是否完成
    if (future.isFinished())
    {
        qDebug() << "Task is finished";
    }
    else
    {
        qDebug() << "Task is still running";
    }
}

handleSingleClick 中,你发射了 itemReClicked 信号:

emit itemReClicked(index, fileloadFuture);

在槽函数中,你可以通过 future.isFinished() 检查任务是否完成。无论任务是否完成,future.isFinished() 的结果始终是正确的,因为 QFuture 的状态是共享的。

总结

  • QFuture 的状态是共享的:即使你将 QFuture 作为值传递,它的状态仍然是共享的。
  • 线程安全QFuture 的状态是线程安全的,你可以在多个线程中安全地访问和修改它的状态。
  • 状态同步:无论你在哪个地方访问 QFuture,它的状态始终是一致的。

因此,即使 fileloadFuture 是按值传递的,你仍然可以在其他函数中通过 isFinished() 检查任务的状态,并且结果始终是正确的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值