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:如果你需要一个更通用、更轻量级的多线程解决方案。
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值