使用QFuture和QFutureWatcher实现不阻塞界面的Async函数

简述

很多时候,在Qt里面需要运行一个耗时函数的时候,为了避免阻塞界面,需要放入非主线程去执行。
实现这样处理的方法有好几种,例如:

  • 写一个继承自QThread类,实现run接口;
  • 写一个继承自QObject的类,添加槽函数执行任务,创建对象,移入一个QThread中进行调用;
  • 写一个QRunnable的子类,创建对象,添加到QThreadPool里面去调用;

上面的方法都比较麻烦些,可以使用QtConcurrent来进行简化。

这里采用基于QtConcurrent的实现,来简化这种异步调用过程,使用QEventLoop来等待执行结束,模拟同步调用。

代码

#include <QDebug>
#include <QEventLoop>
#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrentRun>
#include <functional>
#include <iostream>


template<typename Func, typename... Args>
auto WaitRunAsync(Func&& func, Args&&... args) -> decltype(func(args...))
{
    using ReturnType = decltype(func(args...));

    // 将函数和参数绑定为一个可调用对象
    auto task = std::bind(std::forward<Func>(func), std::forward<Args>(args)...);

    // 使用 QtConcurrent::run 异步执行任务
    QFuture<ReturnType> future = QtConcurrent::run(task);

    // 创建 QFutureWatcher 来监视任务状态
    QFutureWatcher<ReturnType> watcher;

    if (qApp != nullptr) {
        QEventLoop loop;
        // 连接信号槽
        QObject::connect(&watcher, &QFutureWatcher<ReturnType>::finished, &loop, &QEventLoop::quit);
        // 设置 QFutureWatcher 的 future
        watcher.setFuture(future);
        // 使用 QEventLoop 等待任务完成
        loop.exec();
    }
    else {
        // 设置 QFutureWatcher 的 future
        watcher.setFuture(future);
        // 等待计算完成
        watcher.waitForFinished();
    }
    std::cout << "event loop quit!" << std::endl;
    // 返回任务结果
    return future.result();
}

// 示例任务函数
int exampleTask(int a, int b)
{
    std::cout << "Task is running with arguments: " << a << ", " << b << std::endl;
    QThread::sleep(2);   // 模拟耗时操作
    std::cout << "Task completed!" << std::endl;
    return a + b;
}

int main()
{
    std::cout << "Main thread ID: " << QThread::currentThreadId() << std::endl;

    for (int i = 0; i < 3; ++i) {
        std::cout << "run task " << i << std::endl;
        // 异步执行任务并等待完成,获取返回值
        int result = WaitRunAsync(exampleTask, 10, 20);
        std::cout << "Task result: " << result << std::endl;
    }
    std::cout << "Back to main thread!" << std::endl;
    return 0;
}
posted @   乌合之众  阅读(124)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
clear
点击右上角即可分享
微信分享提示