Qt多线程之QtConcurrent

Qt多线程之QtConcurrent


箴言:

《山居秋暝》 — 王维

空山新雨后,天气晚来秋。
明月松间照,清泉石上流。
竹喧归浣女,莲动下渔舟。
随意春芳歇,王孙自可留。

介绍

在 Qt 中,多线程编程是提高应用性能、避免 GUI 阻塞的常用手段。传统方式是继承 QThread,管理线程启动、同步、销毁,但管理复杂、易出错。

QtConcurrent 提供了一个更高级的接口,让我们可以轻松启动并行任务,而无需手动管理线程。本文将从功能、用法到实际案例,带你全面了解 QtConcurrent。

QtConcurrent 是 Qt 提供的 高层次并行编程模块

  • 允许在后台线程执行函数或任务
  • 自动使用线程池管理线程
  • 提供并行算法(map、filter、reduce)
  • 支持任务结果的 QFutureQFutureWatcher,便于异步获取结果

特点:高效、简洁、易用、无需手动管理 QThread

1. 基础用法

1.1 QtConcurrent::run

将一个函数或成员函数 放到后台线程运行自动使用线程池,无需自己管理 QThread。默认是异步的。

同步阻塞调用:

int heavyComputation(int a, int b)
{
    QThread::sleep(2);
    qDebug() << quint64(QThread::currentThreadId());
    return a + b;
}

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

    qDebug() << "主线程: " << quint64(QThread::currentThreadId());

    QFuture<int> future = QtConcurrent::run(heavyComputation, 1, 2);

    qDebug() << future.result();

    qDebug() << "Exit...";

    return a.exec();
}
主线程:  5340
21520
3
Exit...

异步调用:

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

    qDebug() << "主线程: " << quint64(QThread::currentThreadId());


    QFuture<int> future = QtConcurrent::run(heavyComputation, 1, 2);

    QFutureWatcher<int>* watcher = new QFutureWatcher<int>();
    QObject::connect(watcher, &QFutureWatcher<int>::finished, &a, [watcher]()
    {
        qDebug() << "Result: " << watcher->future().result();
        watcher->deleteLater();
    });
    watcher->setFuture(future);

    qDebug() << "Exit...";

    return a.exec();
}

主线程:  20092
Exit...
5956
Result:  3

1.2 QtConcurrent::map

  • 修改原容器
  • 返回 QFuture<void>

同步阻塞调用,每一次调用 容器中的元素,都是不同的线程。

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

    qDebug() << "主线程: " << quint64(QThread::currentThreadId());

    QVector<int> data = {1,2,3,4};

    QFuture<void> future = QtConcurrent::map(data, [](int &v)
    {
        QThread::msleep(100);
        v *= 2;
        qDebug() << v << quint64(QThread::currentThreadId());
    });

    future.waitForFinished();

    qDebug() << "Exit...";

    return a.exec();
}

主线程:  16316
4 18404
6 8872
2 4620
8 14440
Exit...

异步调用

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

    qDebug() << "主线程: " << quint64(QThread::currentThreadId());


    QVector<int> data = {1,2,3,4};

    QFuture<void> future = QtConcurrent::map(data, [](int &v)
    {
        QThread::msleep(100);
        v *= 2;
        qDebug() << v << quint64(QThread::currentThreadId());
    });

    QFutureWatcher<void>* watcher = new QFutureWatcher<void>();
    QObject::connect(watcher, &QFutureWatcher<void>::finished, [&data, watcher]{
        qDebug() << "Done:" << data << quint64(QThread::currentThreadId());
        watcher->deleteLater();
    });

    watcher->setFuture(future);

    qDebug() << "Exit...";

    return a.exec();
}

主线程:  10308
Exit...
4 14824
6 19868
8 10136
2 21612
Done: QVector(2, 4, 6, 8) 10308

1.3 QtConcurrent::mapped + QFuture

不修改原数据 + 返回新结果 + 返回 QFuture<T>

注意,在mapped中的函数对象不能是 Lambda,在Qt5.12.8 版本无法推导类型。

QString mapFunc(int v)
{
    QThread::msleep(100);
    return QString("++ %1 ++ %2").arg(v).arg(quint64(QThread::currentThreadId()));
}


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

    qDebug() << "主线程: " << quint64(QThread::currentThreadId());

    QVector<int> data = {1,2,3,4};

    QFuture<QString> future = QtConcurrent::mapped(data, mapFunc);
    future.waitForFinished();
    QList<QString> result = future.results();

    qDebug() << "Exit..." << result;

    return a.exec();
}

主线程:  12428
Exit... ("++ 1 ++ 21636", "++ 2 ++ 11032", "++ 3 ++ 4496", "++ 4 ++ 20696")

异步调用版本

QFutureWatcher<int>* watcher = new QFutureWatcher<int>();

connect(watcher, &QFutureWatcher<int>::finished, [watcher]{
    auto result = watcher->future().results();
    qDebug() << result;
    watcher->deleteLater();
});

watcher->setFuture(future);

3. 使用案例

3.1 多生产者 + 消费者

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

    qDebug() << "主线程: " << quint64(QThread::currentThreadId());

    USafedQueue<int> queue;

    // 启动多个消费者
    for(int i=0;i<4;++i)
    {
        QtConcurrent::run([&]
        {
            int val;
            while(queue.dequeue(val))
            {
                int result = val * val; // 处理数据
                qDebug() << quint64(QThread::currentThreadId()) << "Consume: Res: " << result << queue.size();
            }
        });
    }

    QtConcurrent::run([&]{
        for(int i=0;i<1000;++i){
            queue.enqueue(i);
            QThread::msleep(10); // 模拟生成速度
            qDebug() << quint64(QThread::currentThreadId()) << "Produce: Res: " << i << queue.size();
        }
        queue.stop(); // 所有生产完成
    });


    qDebug() << "Exit...";

    return a.exec();
}

5248 Consume: Res:  992016 0
2668 Produce: Res:  996 0
3712 Consume: Res:  994009 0
2668 Produce: Res:  997 0
20936 Consume: Res:  996004 0
2668 Produce: Res:  998 0
17040 Consume: Res:  998001 0
2668 Produce: Res:  999 0
posted on 2026-04-04 22:37  Hakuon  阅读(31)  评论(0)    收藏  举报