Qt中的多线程

Qt中的多线程


目录

1 为什么需要多线程
2 Qt中使用多线程的一些注意事项
3 QThread类
  3.1 QThread类的主要接口
  3.2 线程的优先级
4 通过继承QThread类实现多线程
5 从QObject类进行派生实现多线程
5 小结


1 为什么需要多线程

    在现代化的程序设计开发中,多进程、多线程是经常采用的设计方式。在Qt程序中,默认线程(主线程)为窗口线程,当Qt程序在某些情况下需要处理复杂逻辑的时候(比如需要较长时间的网络操作、耗时的数据处理等)可能会占用很长的时间,这时候可能会导致窗口线程响应缓慢(UI响应卡顿),这时候通过多线程设计让多个逻辑事件分配在多个线程中进行操作,并处理好多个线程间的同步与交互,就能极大的提升程序的用户体验和程序执行效率。

2 使用多线程的一些注意事项

    1、Qt中窗口所在的线程为默认主线程,窗口的事件处理和控件的数据更新在此线程进行。

    2、子线程和主线程之间的数据传递主要通过信号槽机制进行。
    3、子线程用于负责非UI部分的后台逻辑,不可操作窗口中的对象。


3 QThread类

    QThread类是Qt提供的一个不依赖于平台的多线程类。

    在使用时,通常会定义一个继承QThread类的自定义类,重定义虚函数run()的实现;用一个从该自定义类实例化的对象来管理一个线程;在主线程中通过对象的start()接口来启动线程(start()接口底层会调用run()实现);在run()实现中调用exit()或者quit()能够结束线程的事件循环,在主线程中调用terminate()可以强制结束线程。
    但也可以通过从QObject类进行派生的方式来实现多线程,这种方式使用起来会更加灵活,但是在代码实现上会更复杂一些。

3.1 QThread类的主要接口

    QThread类是继承自QObject类的,其中主要的一些接口如下:

        1、[公共函数] 判断线程是否结束:bool isFinished()
        2、[公共函数] 判断线程是否正在运行:bool isRunning()
        3、[公共函数] 获得线程的优先级:Priority priority()
        4、[公共函数] 设置线程的优先级:void setPriority(Priority priority)
        5、[公共函数] 退出事件循环:void exit(int returnCode = 0)
        6、[公共函数] 阻塞线程执行直到线程结束,或者time 毫秒:void wait(unsigned long time)
        7、[公共槽函数] 退出线程事件循环:void quit()
        8、[公共槽函数] 根据priority,开始调度、执行线程:void start(Priority priority)
        9、[公共槽函数] 终止线程:void terminate()
        10、[信号] 在线程快结束完成时发射:void finished()
        11、[信号] 在线程调用run()实现前发射:void started()
        12、[静态公共函数] 获得系统可运行的线程的数量:int idealThreadCount()
        13、[静态公共函数] 强制当前线程休眠msecs:int msleep(unsigned long msecs)
        14、[静态公共函数] 强制当前线程休眠secs:int sleep(unsigned long secs)
        15、[静态公共函数] 强制当前线程休眠usecs:int usleep(unsigned long usecs)
        16、[保护函数] 线程执行体虚函数:virtual void run()
        17、[保护函数] 进入线程事件循环:int exec()

3.2 线程的优先级

    在QThread类中声明了从最低到最高的8个优先级(默认情况下为最高优先级QThread::InheritPriority),分别为:

        QThread::IdelPriority
        QThread::LowestPriority
        QThread::LowPriority
        QThread::NormalPriority
        QThread::HighPriority
        QThread::HigestPriority
        QThread::InheritPriority


4 通过继承QThread类实现多线程

    通过自定义一个继承QThread类的自定义类,是在Qt开发中最常见的实现多线程的方式。

    这种方式的优势在于:
        1、可以直接重写run()函数,实现线程的执行逻辑。
        2、可以通过start()函数启动线程,比较直观和简单。
    但这种方式的劣势也很明显:
        1、在多线程中,直接操作QObject派生类的成员可能会导致线程安全问题,需要进行额外的同步处理。
        2、QThread类的创建和销毁需要小心处理,避免资源泄漏和线程未正常退出的情况。
    在具体的操作上,通常要经过如下步骤:
        1、定义一个继承QThread类的自定义类。

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);
protected:
    void run() override;
};

        2、在自定义类中重写父类QThread的run()方法,在该实现中编写子线程的具体逻辑、处理、流程。

void MyThread::run()
{
    // 执行多线程的工作逻辑
    // ...
}

        3、在主线程中,创建一个子线程对象,将自定义的子线程类实例化,并连接适当的信号槽来处理工作完成的事件。

MyThread* thread = new MyThread(this);
connect(thread, &MyThread::finished, this, &MainWindow::handleThreadFinished);

        4、在主线程中,通过创建的子线程对象,调用start(),启动子线程。

thread->start();

        5、在主线程中和子线程,通过信号槽处理线程之间的数据传递、处理逻辑。
        6、在合适的时候,对线程的资源进行合理的释放


5 从QObject类进行派生实现多线程

    通过从QObject类进行派生实现多线程,是在Qt开发中另一种使用较多的实现多线程的方式。

    这种方式的优势在于:
        1、QObject类提供了信号槽机制,方便多线程之间的通信和数据传递。
        2、可以利用Qt的事件循环机制处理事件,例如定时器事件、网络事件等。
        3、可以使用Qt提供的线程安全的容器和工具类,方便进行线程间的数据共享和同步。
    但这种方式的劣势在于:
        1、无法直接重写run()函数,需要使用QRunnable接口或QtConcurrent库来实现线程的执行逻辑。
        2、对象的生命周期和线程的生命周期紧密相关,需要小心处理对象的创建和销毁,避免线程结束后对象仍然存在。
    在具体的操作上,通常要经过如下步骤:
        1、创建一个QObject类的派生类,作为多线程的工作对象

class MyWorker : public QObject
{
    Q_OBJECT
public:
    explicit MyWorker(QObject *parent = nullptr);
public slots:
    void doWork();
signals:
    void workFinished();
};

        2、在创建的QObject类的派生类中,定义一个槽函数 doWork(),用于执行多线程的工作逻辑。

void MyWorker::doWork()
{
    // 执行多线程的工作逻辑
    // ...
    // 工作完成后发射信号
    emit workFinished();
}

        3、在主线程中,创建一个不指定父对象的QThread对象thread,并将QObject派生类的对象worker移动到这个线程对象thread中。

QThread* thread = new QThread;
MyWorker* worker = new MyWorker;
worker->moveToThread(thread);

        4、连接MyWorker对象的 workFinished() 信号到适当的槽函数,以处理工作完成的事件。

QObject::connect(worker, &MyWorker::workFinished, this, &MainWindow::handleWorkFinished);

启动线程并调用 doWork() 函数开始执行多线程工作。

thread->start();
QMetaObject::invokeMethod(worker, "doWork");

        5、通过以上步骤,我们就可以从QObject类派生实现多线程,并利用信号槽机制进行线程间的通信和数据传递了。

    注意,在这个例子中,我们将MyWorker对象移动到了新创建的线程中,并在主线程中通过信号槽机制连接工作完成的信号。这样,当 doWork() 函数执行完成后,会自动发射 workFinished() 信号,触发相应的槽函数执行。


6 小结

    整体而言,从QObject类派生实现多线程会更加的灵活和功能强大,比较适合复杂的多线程应用场景;通过信号槽机制和事件循环,可以方便地实现线程间的通信和处理。而继承QThread类实现多线程相对简单,适用于简单的线程逻辑。在选择时,需要根据具体的需求和应用场景来决定使用哪种方式。

你可能感兴趣的:(嵌入式,学习日记,Qt,qt,嵌入式,多线程,Qt多线程)