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到新线程中

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

Constant

Value

Description

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对应的相同,所以主线程与槽函数的线程是相同的。

---------------------------------------------------------------------------------------------------

概述
    如果想对Qt中的QThread有个更加深刻的了解,必须要知道这几个重要的函数,现在就一一介绍下。

函数介绍
属性    返回值    函数体    功能
static    QThread *    QThread::currentThread()        返回当前线程的指针,静态函数。
static    Qt::HANDLE    QThread::currentThreadId()        返回当前线程的句柄,静态函数
static    bool    QThread::isFinished() const    如果线程执行结束,返回true,否则返回false
static    bool    QThread::isRunning() const    如果当前线程在运行中,返回true,否则返回false
static    int    QThread::idealThreadCount()        返回理理想状态下该系统支持的线程的数量。如果无发现检测到处理器的核数,返回值为-1
protected    int    exec()        使线程进入事件循环状态,并且处于wait状态,直到调用exit()函数使其退出。退出时返回值是调用exit()函数时的输入参数。如果调用quit()函数,其退出的返回值为0.该函数一般在run()函数中调用,使线程进入事件循环处理状态。
protected    void    exit(int returnCode = 0)        告知线程从事件循环状态退出,并且返回returnCode的值。一般说来,返回0表示成功退出,返回非0值表示遇到错误。调用该函数后,线程不会再进行事件处理,除非再次调用exec()函数。如果当前线程不处于执行状态,那么下次调用exec()也会直接返回
private signal    void    finished()        在线程执行完毕前发出该信号,当发出该信号时,意味着线程早已经退出了事件循环处理状态,即不再处理除了延迟删除事件(deferred deletion)之外的任何事件。可以把该信号和QObject::deleteLater()连接起来用来删除线程中的对象。如果强制使用terminate()函数来结束线程,那么将无法得知finish()信号的发送线程。另外,这是一个私有信号,所以用户无法发出这个信号。
static    bool    QThread::isInterruptionRequested() const        返回当前线程上运行的任务是否可以停止,可以通过requestInterruption()来进行中断查询。该函数可以使得长时间运行的任务彻底停止,永远不要检查该函数的返回值是否是安全的。但是进行长时间任务时提倡有规律的使用该方法。但是不要频繁使用该方法避免线程切换的开销。
static    bool    QThread::isRunning() const        返回当前线程是否正在运行,如果在运行,返回true,否则返回false
static    int    QThread::loopLevel() const        该函数返回当前事件循环的层级,但是该函数只能在线程内部调用。
static    void    QThread::msleep(unsigned long msecs)        强制线程休息msecs毫秒
static    void    QThread::usleep(unsigned long usecs)        强制线程休息usecs微秒
static    void    QThread::msleep(unsigned long secs)        强制线程休息secs秒
virtual protected    void    QThread::run()        该函数是线程运行的起始点。调用start()函数之后,新创建的函数调用该函数,默认的QThread简单的调用exec()进入事件循环机制。我们可以重载这个函数来实现更高级的线程管理方式。从该函数返回将结束线程。
static    void    QThread::setPriority(Priority priority)        该函数设置线程运行的优先级,如果线程并没有处于运行状态,该函数什么也不做。可以通过调用start()来指定一个优先级来启动线程。优先级的效果依赖于操作系统的调度方式,尤其是在不支持优先级的操作系统上,优先级设置会被忽略。
slot    void    QThread::quit()        告诉线程的事件循环退出并且返回0值,相当于调用QThread::exit(0)。如果线程没有事件循环,这个函数则什么也不做。
virtual protected    void    QThread::setStackSize(uint stackSize)        设置线程堆栈的最大值,如果设置的值大于0,那么就会将堆栈最大值设置为当前数值。否则,线程堆栈的最大值由操作系统来决定。警告:大部分操作系统都会自己设置线程堆栈的最大最小值,如果设置的堆栈大小超出范围,线程则不能启动。
static protected    void    QThread::setTerminationEnabled(bool enabled = true)    
slot    void    QThread::start(Priority priority = InheritPriority)        通过调用run()函数启动线程。操作系统会根据优先级来调度线程。如果线程已经处于运行状态,该函数什么也不做。优先级设置依赖于操作系统的线程调度方式。
signal    void    QThread::started()        线程开始执行时发出该信号,发出时间在run()函数调用之前。注意:这是一个私有信号,因此只能由线程发出,用户不能发出该信号。
slot    void    QThread::terminate()        终止当前线程。线程或许不会立即被终止,依赖于线程的调度策略。一般情况下,调用该函数之后再调用QThread::wait()来确保线程结束。该线程终止后,等待该线程的其他线程将被唤醒。警告:该函数比较危险,不推荐这样做。线程可能在任何代码处终止。或许在修改数据时被终止,线程结束后自己不能去做清理工作。
static    bool    QThread::wait(unsigned long time = ULONG_MAX)        阻塞当前的进程,直到满足如下两个条件之一: 1.相关的线程完成其任务,然后如果线程已经结束,则该函数返回true,如果线程没有启动,则该函数也会返回true。 2. 经过了特定长度的时间,如果时间是ULONG_MAX(默认值),那么wait()函数几乎不会超时。(即该函数必须从run()函数返回)如果wait函数超时,那么该函数会返回false。
static    void    QThread::yieldCurrentThread()    将当前线程的执行权让给别的可执行线程。至于让给哪一个可执行线程,那就是操作系统的事情了。
使用技巧
一般调用quit()函数之后可以紧接着调用wait()函数确保线程退出。
sleep()等让线程休眠的函数不需要调用,因为Qt中线程是事件驱动机制。但是如果是继承的QTHread类,在run()函数中使用了无限循环的方式,可以考虑msleep()函数来使线程休息一段时间,一般为1毫秒。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值