• 【Qt开发】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::AutoConnectionQt::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的槽函数,那么结论同上!



    注:
        技术在于交流、沟通,转载请注明出处并保持作品的完整性。
  • 相关阅读:
    实时数据库(时序数据库)——apacheiotdb
    Tablesaw——Java统计、机器学习库
    smile——Java机器学习引擎
    SVGEdit——SVG编辑器
    Bika LIMS 开源LIMS集—— SENAITE的使用(仪器管理的使用)
    SPC(Statistical Process Control 统计过程控制)图——Python+JS实现
    Bika LIMS 开源LIMS
    FUXA——基于Web的过程可视化软件
    Bika LIMS 开源LIMS集—— SENAITE的使用(材料管理的使用)
    效果演示
  • 原文地址:https://www.cnblogs.com/huty/p/8518243.html
Copyright © 2020-2023  润新知