• QThread详解


    回顾Qt之线程(QThread),里面讲解了如何使用线程,但还有很多人留言没有看明白,那么今天我们来一起瞅瞅关于QThread管理线程的那些事儿。。。


    一、线程管理

    1、线程启动

    void start(Priority priority = InheritPriority)

    调用后会执行run()函数,但在run()函数执行前会发射信号started(),操作系统将根据优先级参数调度线程。如果线程已经在运行,那么这个函数什么也不做。优先级参数的效果取决于操作系统的调度策略。特别是那些不支持线程优先级的系统优先级将会被忽略(例如在Linux中,更多细节请参考http://linux.die.net/man/2/sched_setscheduler)。

     

    2、线程执行

    int exec()

    进入事件循环并等待直到调用exit(),返回值是通过调用exit()来获得,如果调用成功则范围0。

    virtual void run();

    线程的起点,在调用start()之后,新创建的线程就会调用这个函数,默认实现调用exec(),大多数需要重新实现这个功能,便于管理自己的线程。该方法返回时,该线程的执行将结束。

     

    3、线程退出

    void quit()

    告诉线程事件循环退出,返回0表示成功,相当于调用了QThread::exit(0)。

    void exit(int returnCode = 0)

    告诉线程事件循环退出。

    调用这个函数后,线程离开事件循环后返回,QEventLoop::exec()返回returnCode,

    按照惯例0表示成功,任何非0值表示失败。

    void terminate()

    终止线程,线程可能会立即被终止也可能不会,这取决于操作系统的调度策略,使用terminate()之后再使用QThread::wait()确保万无一失。

    当线程被终止后,所有等待中的线程将会被唤醒。

    警告:此功能比较危险,不鼓励使用。线程可以在代码执行的任何点被终止。线程可能在更新数据时被终止,从而没有机会来清理自己,解锁等等。。。总之,只有在绝对必要时使用此功能。

    建议:一般情况下,都在run函数里面设置一个标识符,可以控制循环停止。然后才调用quit函数,退出线程。

     

    4、线程等待

    void msleep(unsigned long msecs)

    强制当前线程睡眠msecs毫秒

    void sleep(unsigned long secs)

    强制当前线程睡眠secs

    void usleep(unsigned long usecs)

    强制当前线程睡眠usecs微秒

    bool wait(unsigned long time = ULONG_MAX);

    线程将会被阻塞,等待time毫秒。和sleep不同的是,如果线程退出,wait会返回。

     

    5、线程状态

        bool isFinished() const    

        线程是否结束

        bool isRunning() const    
        线程是否正在运行
     

    6、线程优先级

        void setPriority(Priority priority)
        这个函数设置正在运行线程的优先级。如果线程没有运行,此功能不执行任何操作并立即返回。使用的start()来启动一个线程具有特定的优先级。
        优先级参数可以是QThread::Priority枚举除InheritPriortyd的任何值。
        Priority priority() const
        下面来看下优先级中的各个枚举值:

    Constant

    Value

    Description

    QThread::IdlePriority

    0

    没有其它线程运行时才调度.

    QThread::LowestPriority

    1

    比LowPriority调度频率低.

    QThread::LowPriority

    2

    比NormalPriority调度频率低.

    QThread::NormalPriority

    3

    操作系统默认的默认优先级.

    QThread::HighPriority

    4

    比NormalPriority调度频繁.

    QThread::HighestPriority

    5

    比HighPriority调度频繁.

    QThread::TimeCriticalPriority

    6

    尽可能频繁的调度.

    QThread::InheritPriority

    7

    使用和创建线程同样的优先级. 这是默认值.

     
     
    二、主线程、次线程

    Qt之线程(QThread)一节中我介绍了QThread 的两种使用方法:

    1、子类化 QThread(不使用事件循环)

    这是官方手册、例子以及相关书籍中都介绍的一种常用的方法。

    a. 子类化 QThread

    b. 重载 run 函数,run函数内有一个while或for的死循环(模拟耗时操作)

    c. 设置一个标记为来控制死循环的退出。

     

    2、子类化 QObject

    a. 子类化 QObject

    b定义槽函数

    c. 将该子类的对象moveToThread到新线程中

     

    run 对于线程的作用相当于main函数对于应用程序。它是线程的入口,run的开始和结束意味着线程的开始和结束。

    采用这两种做法,毫无疑问都会在次线程中运行(这里说的是,run中的逻辑以及子类化QObject后连接通过moveToThread然后连接到QThread的started()信号的槽函数,这个下面会详细讲解)。

    那么,线程中的槽函数是怎么运行的呢?

    说到信号与槽,大家应该再熟悉不过了,包括我,特别喜欢使用自定义信号与槽,感觉用起来特方便、特棒。。。

    经常使用,你能否100%的使用正确?你了解它的高级用法吗?

    1、你是否在多次connect,还发现不了为什么槽函数会执行那N多次。

    2、你是否了解disconnect

    3、你是否了解connect中的第五个参数 Qt::ConnectionType

    关于connect、disconnect信号、槽的使用可参考:Qt之信号与槽。既然谈到线程这里需要重点说下Qt::ConnectionType(信号与槽的传递方式)

    Constant

    Value

    Description

    Qt::AutoConnection

    0

    自动连接:(默认值)如果信号在接收者所依附的线程内发射,则等同于直接连接。如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接。

    Qt::DirectConnection

    1

    直接连接:当信号发射时,槽函数将直接被调用。无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。

    Qt::QueuedConnection

    2

    队列连接:当控制权回到接受者所依附线程的事件循环时,槽函数被调用。槽函数在接收者所依附线程执行。也就是说:这种方式既可以在线程内传递消息,也可以跨线程传递消息

    Qt::BlockingQueuedConnection

    3

    与Qt::QueuedConnection类似,但是会阻塞等到关联的slot都被执行。这里出现了阻塞这个词,说明它是专门用来多线程间传递消息的。

     

    举例:

    MyObject.h
    #ifndef MYOBJECT_H
    #define MYOBJECT_H
    
    #include 
    
    class MyObject : public QObject
    {
        Q_OBJECT
    
    public:
        explicit MyObject(QObject *parent = 0);
    
    public slots:
        void start();
    };
    
    #endif // MYOBJECT_H
    MyObject.cpp
    #include "MyObject.h"
    #include 
    #include 
    
    MyObject::MyObject(QObject *parent)
        : QObject(parent)
    {
    
    }
    
    void MyObject::start()
    {
        qDebug() << QString("my object thread id:") << QThread::currentThreadId();
    }
    
    main.cpp
    #include "MyObject.h"
    #include 
    #include 
    #include 
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        qDebug() << QString("main thread id:") << QThread::currentThreadId();
    
        MyObject object;
        QThread thread;
        object.moveToThread(&thread);
        QObject::connect(&thread, SIGNAL(started()), &object, SLOT(start()));
        thread.start();
    
        return a.exec();
    } 
    查看运行结果:

      "main thread id:" 0xf08

      "my object thread id:" 0x216c

        显然主线程与槽函数的线程是不同的(你可以多次尝试,屡试不爽。。。),因为moveToThread后MyObject所在的线程为QThread,继上面介绍的thread.start()执行后首先会发射started()信号,也就是说started()信号发射是在次线程中进行的,所以无论采取Qt::AutoConnection、Qt::DirectConnection、Qt::QueuedConnection哪种连接方式,主线程与槽函数的线程都是不同的。

    1、修改代码如下:

        MyObject object;
        QThread thread;
        //object.moveToThread(&thread);
        QObject::connect(&thread, SIGNAL(started()), &object, SLOT(start()), Qt::DirectConnection);
        thread.start();
    查看运行结果:

      "main thread id:" 0x2688

      "my object thread id:" 0x2110 

        显然主线程与槽函数的线程是不同的,MyObject所依附的线程为主线程(因为注释掉了moveToThread),继上面介绍的Qt::DirectConnection(无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行)。也就是说started()信号发射是在次线程中进行的,槽函数也是在次线程中进行的,所以主线程与槽函数的线程是不同的。

    2、修改代码如下:

        MyObject object;
        QThread thread;
        //object.moveToThread(&thread);
        QObject::connect(&thread, SIGNAL(started()), &object, SLOT(start()), Qt::QueuedConnection);
        thread.start();

    查看运行结果:

      "main thread id:" 0x24ec

      "my object thread id:" 0x24ec 

        显然主线程与槽函数的线程是相同的,继上面介绍的Qt::QueuedConnection(槽函数在接收者所依附线程执行)。也就是说started()信号发射是在次线程中进行的,但MyObject所依附的线程为主线程(因为注释掉了moveToThread),所以主线程与槽函数的线程必然是相同的。

    3、修改代码如下:

        MyObject object;
        QThread thread;
        //object.moveToThread(&thread);
        QObject::connect(&thread, SIGNAL(started()), &object, SLOT(start()), Qt::AutoConnection);
        thread.start();
    查看运行结果:

      "main thread id:" 0x2700

      "my object thread id:" 0x2700 

        显然主线程与槽函数的线程是相同的,MyObject所依附的线程为主线程(因为注释掉了moveToThread),继上面介绍的Qt::AutoConnection(如果信号在接收者所依附的线程内发射,则等同于直接连接。如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接。)。因为started()信号和MyObject依附的线程不同,所以结果和Qt::QueuedConnection对应的相同,所以主线程与槽函数的线程是相同的。

        基本就介绍到这里,QThread使用和上面的大同小异,run里面执行的代码都是在次线程中,如果是QThead的槽函数,那么结论同上!

  • 相关阅读:
    【Leetcode】23. Merge k Sorted Lists
    【Leetcode】109. Convert Sorted List to Binary Search Tree
    【Leetcode】142.Linked List Cycle II
    【Leetcode】143. Reorder List
    【Leetcode】147. Insertion Sort List
    【Leetcode】86. Partition List
    jenkins 配置安全邮件
    python 发送安全邮件
    phpstorm 同步远程服务器代码
    phpUnit 断言
  • 原文地址:https://www.cnblogs.com/senior-engineer/p/9758756.html
Copyright © 2020-2023  润新知