【QT】QtConcurrent的使用介绍,与std::thread的区别

发布于:2024-04-26 ⋅ 阅读:(26) ⋅ 点赞:(0)

QtConcurrent 模块是 Qt 框架中用于简化并发编程的一部分。它提供了一系列高级API,使得开发者能够更容易地编写多线程代码,从而利用多核处理器的能力。这个模块主要围绕使用线程池来执行函数调用、运行算法或者处理数据集。QtConcurrent 的核心优势是它的简洁性,因为它隐藏了线程的创建、管理和数据同步的复杂性。

核心功能

QtConcurrent 包含以下几个核心功能:

  • QtConcurrent::run:用于在后台线程中运行函数或成员函数。
  • QtConcurrent::map, mapReduced, reduce:用于对集合中的元素并行执行操作。
  • QtConcurrent::filter:用于并行过滤容器中的元素。

如何使用

  1. 简单的后台执行:使用 QtConcurrent::run 来异步执行函数。

  2. 处理集合:使用 map, reduce 等函数处理数据。

  3. 等待异步操作完成:使用 QFutureQFutureWatcher 来监控和同步异步操作的结果。

示例代码

下面是一些使用 QtConcurrent 模块的例子:

1. 使用 QtConcurrent::run 执行无参函数

假设有一个简单的函数,您希望在后台线程中运行它:

void myFunction() {
    qDebug() << "Function running in background thread";
}

// 在某个函数中
QFuture<void> future = QtConcurrent::run(myFunction);
2. 使用 QtConcurrent::map 对集合应用函数

如果您想对一个 QList 中的每个元素执行某个操作,可以使用 map

void square(int &value) {
    value *= value;
}

// 在某个函数中
QList<int> list = {1, 2, 3, 4, 5};
QtConcurrent::map(list, square);
3. 使用 QtConcurrent::filter 过滤数据

过滤数据的例子:

bool isOdd(int value) {
    return value % 2 != 0;
}

// 在某个函数中
QList<int> list = {1, 2, 3, 4, 5};
QtConcurrent::filter(list, isOdd);
4. 使用 QtConcurrent::reduce 合并结果

合并结果,如计算总和:

int add(int x, int y) {
    return x + y;
}

// 在某个函数中
QList<int> list = {1, 2, 3, 4, 5};
int result = QtConcurrent::blockingReduce(list, add, 0);  // 初始值为 0

注意事项

  • 使用 QtConcurrent 时,需要注意线程安全和数据访问冲突。
  • 确保在 .pro 文件中加入 concurrent 配置:QT += concurrent
  • 使用 QFutureWatcher 可以监视 QFuture 的状态,如进度、完成状态等。

QtConcurrent 为多线程编程提供了一个更简单、更高级的接口,极大地降低了多线程编程的难度,并能有效利用现代多核CPU的计算能力。

题外话:

在 Qt 中使用 qDebug() 结合 QThread::currentThread() 输出类似于 QThread(0x73ed260, name = "Thread (pooled)") 的信息时,它显示的是当前正在执行代码的线程的一些基本信息。

这段输出具体说明了:

  1. 内存地址0x73ed260 是该线程对象在内存中的地址。
  2. 线程名称"Thread (pooled)" 表示线程的名称。这里的 “pooled” 暗示该线程是一个线程池中的线程。

解释

  • QThread(0x73ed260, name = “Thread (pooled)”)
    • QThread:是 Qt 中用于线程操作的类。
    • (0x73ed260):这部分是该 QThread 对象的内存地址,用于唯一标识这个对象实例。
    • name = "Thread (pooled)":表示线程的名称,通常线程池中的线程会被标记为 “pooled”,表明它们是由 Qt 的线程池管理的,而不是独立创建的线程。

线程池和 QtConcurrent

当你使用 QtConcurrent 运行函数时,Qt 通常会利用内部的线程池(除非指定了其他线程池),这些线程池中的线程具有 “Thread (pooled)” 的标识。这是 Qt 管理线程资源和优化性能的一种方式,线程池可以复用活跃的线程,避免了频繁创建和销毁线程的开销。

实际应用

在多线程编程中,了解当前代码运行在哪个线程上是非常重要的,特别是在处理 GUI 更新或其他需要在特定线程(如主线程)上执行的操作时。Qt 中的 GUI 组件大多不是线程安全的,因此更新 GUI 元素通常需要确保在主线程上进行。

如果您在使用 QtConcurrent 或其他多线程工具时需要确保某些操作返回主线程执行(比如更新用户界面),您可以使用 QMetaObject::invokeMethod 或通过信号和槽机制来安全地将操作调度回主线程。例如:

// 例子:在主线程中安全更新 GUI
QMetaObject::invokeMethod(this, "updateGui", Qt::QueuedConnection);

这种方式可以保证 updateGui() 方法在主线程(通常是 GUI 线程)中被调用,从而安全地进行 UI 更新。

std::thread

在 Qt 和标准 C++ 中处理多线程时,QtConcurrent::runstd::thread 提供了不同的方法和功能,每种方法都有其独特的特点和最佳用途。

QtConcurrent::run

  • 管理和简化QtConcurrent::run 是 Qt 框架提供的高级多线程接口,旨在简化线程的使用和管理。它自动处理线程的创建、执行和销毁,并使用 Qt 的内部线程池来管理线程。这减少了直接管理线程的复杂性,并优化了线程使用,因为线程池可以根据需要复用线程。
  • 集成:它与 Qt 的事件系统和信号槽机制紧密集成,可以方便地将线程执行结果与 Qt 的主事件循环结合,通过 QFutureQFutureWatcher 管理和监视异步操作。
  • 使用示例
    QtConcurrent::run(functiontimerused, &FunctionTimerUsed::UsedTimer);
    

std::thread

  • 标准库支持std::thread 是 C++11 标准库的一部分,提供了基本的线程功能。它允许开发者直接控制线程的创建和执行,提供了更精细的线程管理能力。
  • 灵活性:使用 std::thread,开发者可以直接控制线程的生命周期和同步机制。在某些需要精细控制线程行为的场合,这种能力非常重要。
  • 分离线程:通过调用 detach(),可以让线程在后台独立执行,这意味着线程一旦启动就与主线程分离,主线程不会等待其完成。这在某些长时间运行的操作中很有用,但同时也增加了管理分离线程的复杂性。
  • 使用示例
    std::thread threadTimerStart_7(&FunctionTimerUsed::UsedTimer, functiontimerused);
    threadTimerStart_7.detach();
    

区别总结

  1. 线程管理

    • QtConcurrent::run 使用 Qt 的线程池,对于多数应用场景来说,这简化了线程的使用并优化了资源管理。
    • std::thread 提供更基础和直接的线程控制,适用于需要手动管理线程生命周期的情况。
  2. 集成与兼容性

    • QtConcurrent 与 Qt 的其他部分(如事件循环)集成良好,特别适用于 Qt 应用程序。
    • std::thread 是 C++标准的一部分,可以在任何 C++项目中使用,不依赖于 Qt。
  3. 错误处理和安全性

    • 使用 QtConcurrent 可以较容易地通过 QFuture 监视线程状态,而使用 std::thread 通常需要开发者自行处理线程同步和错误管理。

根据具体需求选择合适的工具,如果是在 Qt 环境中并且不需要精细控制线程行为,QtConcurrent 可能是更合适的选择。如果您需要跨平台或不依赖 Qt 的解决方案,或者需要对线程有完全的控制,那么 std::thread 可能更适合。