QThread和std::thread

        在 Qt 中, 我们经常会用到多线程,这时候就需要纠结是使用 Qt 的 QThread 还是使用 C++ 标准库的 std::thread。

        这里记录一下我自己的理解,先介绍一下 QThread 和 std::thread 的使用方法,对比一下他们的不同,最后说一下我理解的应该怎么选择这两种方法。


QThread使用方法

        QThread 的使用有两种方法,一种是继承 QThread,然后重写 run() 函数,这种方法适合执行单个任务,比如计算某个耗时操作,执行完即可,不需要线程常驻。在 Qt5 之后 Qt 官方已经不推荐这种使用方法了,所以在这里也不赘述了。

        在 Qt5 之后,推荐的方法是创建一个工作类继承于 QObject,将工作对象通过 movetothread() 函数移动到一个新的线程中,这种方法更符合 Qt 的对象模型,并且更易于管理线程的生命周期和资源。下面是一个示例:

class Worker : public QObject {
    Q_OBJECT
public:
    Worker();
    ~Worker();

    void doWorkA() {
        qDebug() << "Worker thread ID:" << QThread::currentThreadId();
        emit sigReadData("doWorkA");
    }

    void doWorkB() {
        qDebug() << "Worker thread ID:" << QThread::currentThreadId();
        emit sigResultReady();
    }

signals:
    void sigReadData(QString data);
    void sigResultReady();
};
QThread *thread = new QThread();
Worker *worker = new Worker();

worker->moveToThread(thread);

// 连接信号槽
QObject::connect(worker, &Worker::sigReadData, this, &::);
QObject::connect(worker, &Worker::sigResultReady, thread, &QThread::quit);
QObject::connect(worker, &Worker::sigResultReady, worker, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); // 线程完成后自动删除

qDebug() << "Main thread ID:" << QThread::currentThreadId();

thread->start();
worker->doWorkA();
worker->doWorkB();

        这个 Worker 类没有做任何特别的事情,但它包含所有必需的元素。在这个例子中, doWorkA() 被调用时可以做一些你自己的处理,处理完后发送 sigReadData() 给主线程,doWorkB() 完成后它会发出信号 sigResultReady(),然后该信号将连接到 QThread 的槽 quit(),触发 QThread 的事件循环退出。事件循环退出后,QThread::finished() 信号被触发,连接到槽 QObject::deleteLater(),自动删除 QThread 对象。

        顺便说一句,这里要注意的非常重要的一件事是你永远不应该在 QObject 类的构造函数中分配堆对象(使用 new)。如果在构造函数中new,会报错 QObject:Cannot create children for a parent that is in a different thread。这个报错是由于这个 new 分配是在主线程,而不是新的子线程 QThread 实例上的。这意味着新创建的对象是由主线程拥有的,而不是 QThread 实例。所以,应该在 Worker 类的函数或者槽函数中分配此类资源,例如在 doWorkA() 或者doWorkB() 中使用new。在这种情况下,当调用该对象时,该对象将位于新线程实例上,因此新的线程实例将拥有该资源。


std::thread使用方法

        std::thread 是 C++11 引入的标准线程库,用于创建和管理线程。它提供了一种轻量级的方法来实现多线程编程,适合需要高性能和低延迟的应用。以下是 std::thread 的基本使用方法,可以使用下面几种方法创建线程:

#include <iostream>
#include <thread>

class MyClass {
public:
    void memberFunction() {
        std::cout << "Hello from member function!" << std::endl;
    }
};

void threadFunction() {
    std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
}

int main() {
    // 使用函数指针
    std::thread t1(threadFunction);
    std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
    if (t1.joinable()) {
        t1.join();    // 等待线程完成
    }

    // 使用成员函数
    MyClass obj;
    std::thread t2(&MyClass::memberFunction, &obj); // 传递对象指针
    if (t2.joinable()) {
        t2.join();    // 等待线程完成
    }

    // 使用lambda表达式
    std::thread t3([](){
        std::cout << "Hello from thread!" << std::endl;
    });
    if (t3.joinable()) {
        t3.join();    // 等待线程完成
    }

    return 0;
}

        管理线程有两个函数,一个是上面已经使用过的 join(),另一个是 detach():

std::thread t(threadFunction);
t.join(); // 阻塞主线程,直到 t 线程完成
std::thread t(threadFunction);
t.detach(); // 将线程与当前线程分离,允许 t 独立运行
// 注意:必须确保分离的线程在程序退出前完成


QThread和std::thread的选择

        在我看来,QThread 不仅仅是一个简单的线程类,它更像一个线程管理器。提供了与 Qt 框架的无缝集成,特别是与信号槽机制的结合使用,使得多线程编程更加方便和高效。我们可以把部分功能封装在一个工作对象中,然后把这个对象 movetothread,这样通过这个对象调用封装的所有方法,都是在子线程中进行,我只需要通过Qt的信号槽把子线程中我需要的数据传出来接收就可以在主线程或者UI上显示,而不会卡住主线程或者UI。

        例如,我要控制一个打印机或者串口,需要连接设备、发送消息和一直接收消息等功能,我们把这几个功能封装在一个打印机类或者串口类中。创建对象,然后通过 movetothread() 把这个类放到子线程,这样我们就可以把连接、发送接收处理消息这些功能都放到子线程中,而不影响主线程,接收到的消息需要传给主线程并显示的话就发送信号给主线程。

        而对于一些简单的多线程任务,例如并行计算、独立的后台任务等,使用 std::thread 可以更加轻便、直接和高效。std::thread 相对轻量,没有额外的框架依赖,适用于需要高性能和低开销的多线程操作。并且,std::thread 是 C++ 标准的一部分,保证了跨平台的一致性。如果需要在不同的平台上开发和运行代码,std::thread 提供了一种标准化的方法。

        总结一下:

  • 选择 QThread:如果你需要与 Qt 对象和事件循环进行集成。
  • 选择 std::thread:如果你需要一个更通用、更轻量级的多线程解决方案。

相关推荐

  1. QThreadstd::thread

    2024-07-17 09:40:05       23 阅读
  2. QT6之多线程——子类化QObject子类化QThread

    2024-07-17 09:40:05       23 阅读
  3. 【QT】QThread 成员函数

    2024-07-17 09:40:05       39 阅读
  4. QThread线程退出

    2024-07-17 09:40:05       42 阅读
  5. Qt之多线程编程(QThread)

    2024-07-17 09:40:05       25 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 09:40:05       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 09:40:05       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 09:40:05       58 阅读
  4. Python语言-面向对象

    2024-07-17 09:40:05       69 阅读

热门阅读

  1. 什么是网络存储服务器

    2024-07-17 09:40:05       23 阅读
  2. 特殊类设计

    2024-07-17 09:40:05       26 阅读
  3. Spring MVC-11

    2024-07-17 09:40:05       22 阅读
  4. Linux 驱动开发 举例

    2024-07-17 09:40:05       17 阅读
  5. ES6及ESNext规范

    2024-07-17 09:40:05       25 阅读
  6. Mysql---基础篇

    2024-07-17 09:40:05       26 阅读
  7. 前端数据加密方式

    2024-07-17 09:40:05       24 阅读
  8. Oracle(2)Oracle数据库的主要组件有哪些?

    2024-07-17 09:40:05       22 阅读
  9. 大语言模型 API

    2024-07-17 09:40:05       23 阅读