Qt 线程

虽然 QT 针对线程做了一刀切,只有主线程能够对窗口的控件进行操作,不过在进行 IO 密集操作时,还是需要线程的存在的,因此我们本文来学习 Qt 的线程。

QThread类

Qt 线程是基于 QThread 类实现的,基本原理和 C++的 std::thread 类差不多。

我们先来了解一下 QThread 类的 API。

QThread类 API

run()线程的入口函数,即线程主要执行的内容
start()线程的启动函数,调用该函数后会自动进入 run() 函数来执行线程
currentThread()返回一个指向管理当前线程的 QThread 的指针
isRuning()线程是否在运行,在运行返回 true,否则 false
sleep()/msleep()/usleep()线程休眠函数,单位分别是 秒/ 毫秒 / 微秒
wait()

阻塞线程,直到满足下面任一条件

与 QThread 对象关联的线程已经完成执行(从 run 函数返回)。如果线程已经完成,则返回 true,如果线程尚未启动,则返回 true

已经过了几毫秒,如果时间是 ULONG_MAX(默认值),那么等待永远不会超时(此时线程必须从 run() 函数返回),而如果等待超时,则返回 false

与Linux 的 pthread_join() 函数类似

terminate()终止线程的执行。线程可能立即终止,也可能不会,取决操作系统的调度策略
finished()当线程结束时会发出该信号,通过该信号来实现线程的清理工作。

QT 线程的使用方式

和 C++ 的不同,QT 的线程的使用方式和事件的使用方式类似,即通过多态来重写 run 函数,这样才能使用 QT thread。

这里我们来演示一遍利用 QThread 来进行定时器的设计。

首先创建好项目后,再创建一个类项目,该类继承自 QThread 类,并且重写 run 函数。

 然后我们在 QT Designer 界面拖拽一个 lcdnumber 控件,并设置好初始值。

 

 接着我们实现 run 函数的内容,因为要计时 10 秒,因此我们设计一个循环,每循环一次线程就休眠一秒,休眠一秒后线程就发送一个 notify 的信号——该信号由我们自己编写。

 这样线程的 run 函数就实现了,接着我们来使用该线程。

首先是在类内部添加一个线程对象。

然后再启动线程,并且将线程的信号和指定的函数连接在一起。

比如 notify 信号是我们自定义的信号,表明线程已经休眠了一秒。

而 finished 信号是线程自带的信号,表明线程已经运行完了。 

 然后实现两个函数。

handler() 执行 计时器的操作

handler2() 执行线程的等待操作。

可以看到,确实实现了计时器功能,并且在线程从 run 函数返回时,也执行了 wait 函数,释放线程控件。 

 

connect 函数的第五个参数 

在 QT 文档中查看 connect 函数的参数,发现除了发送者、发送信号、接受者、接收函数之外,还有第五个参数: Qt::ConnectionType type .

这第五个参数指信号和槽的连接类型,只有在多线程的情况下有用。 

它由五种连接类型。

Qt::AutoConnection

会根据信号和槽函数所在线程自动选中类型。

如果信号和槽在同一个线程,则是 Qt::DirectConnection 类型;否则是 Qt::QueuedConnection 类型。        

Qt::DirectConnection当信号发出时,槽函数会在同一线程立即执行,适用于 信号和槽在同一线程的情况,要注意线程安全。
Qt::QueuedConnection当信号发出时,槽函数会插入到接收对象所在的线程的事件队列中,等待下一次事件循环时执行。适用于信号和槽在不同线程的情况,可以确保线程安全。
Qt::BlockingQueuedConnection该连接类型和 QueuedConnection 类似,不过发送信号的线程会被阻塞,直到槽函数执行完毕适用于需要等待槽函数执行完毕再继续的常见,但要考虑死锁的问题
Qt::UniqueConnection一个标志,可以通过 位或 与上述任意类型组合使用

QMutex类 

为了保证线程安全,Qt 自然也是有互斥锁的存在。

它的使用方式和 std::mutex 类似。

lock()互斥锁上锁,其他争夺资源失败的线程会被挂起
unlock()互斥锁解锁,其他线程会被唤醒

QMutexLocker类

和智能指针类似,采用 RAII 机制进行解锁和上锁。

在其作用域自动上锁,出了作用域自动解锁。

void run()

{

        QMutexLocker locker(mutex) //这样就会自动上锁

        //...

        //...

        出了作用域自动解锁

}

QReadWriteLocker、QReadLocker、QWriteLocker类

这三个锁分别是 读写锁、读锁和写锁。

读写锁:用于控制读和写的并发访问

读锁 : 用于给读操作上锁,运行多线程并发读,不过不允许有线程写

写锁:用于给写操作上锁,只允许一个线程访问资源

读锁和写锁的使用方法和 QMutexLocker 类似,QReadWriteLocker 类似于 QMutex。

QReadWriteLocker rwlock;

//read

{

        //自动上读锁

        QReadLocker rlock(&rwlock);

        //在作用域结束时解锁

}

//write

{

        //自动上写锁

        QWriteLocker wlock(&rwlock);

        //在作用域结束时解锁

}

条件变量

有互斥自然也有同步,QT的条件变量是 QWaitCondition 类,利用其函数到达同步的目的。

API作用
QWaitCondition()构造函数
~QWaitCondition()析构函数
bool wait(QMutex* mutex,unsigned long time = ULONG_MAX)

解锁mutex,并且使调用该函数的线程阻塞,满足下面两个条件才能被唤醒:

1:其他线程调用 wakeOne() 或者 wakeAll()

2: time 毫秒过去

wait ( QReadWriteLock * readWriteLock, unsigned long time = ULONG_MAX )和上面类似,不过针对 QReadWriteLock 锁
void wakeAll()唤醒所有等待的线程,唤醒顺序不定
void wakeOne()唤醒随机一个进程,顺序不定
void notify_all()同 wakeAll()
void notify_one()同 wakeOne()

通过这些函数,能够实现线程的同步和互斥操作。

QMutex mutex;
QWaitCondition cond;

//抢夺互斥锁后
mutex.lock();

//检测是否满足条件
while(!(自带的条件)) cond.wait(&mutex);

//满足条件后继续执行
mutex.unlock();

mutex.lock();

//继续执行自己的的代码

cond.wakeAll()/wakeOne();

mutex.unlock();

信号量

和 linux 的 sem 信号量一样,Qt 的信号量也是用于同步操作的。

QT 的信号量类是 QSemaphore 类。

API描述
QSemaphore(int n = 0)构造函数
~QSemaphore()析构函数
void acquire(int n = 1)

来请求 n 个信号量,如果 信号量 >= n ,则请求成功了,信号量 - n,如果 信号量 < n  则该线程阻塞,直到信号量的值 >= n;

int available() conset返回当前可用信号量的值
void release(int n = 1)

用于释放 n 个信号量,如果n < 信号量的最大值,则信号量会自动+n,而如果 n > 信号量的最大值,则会自动创建多个信号量

bool tryAcquire(int n = 1)尝试请求 n 个信号量,如果信号量不足,直接返回false,如果足够,则请求成功, 返回 true
bool tryAcquire(int n = 1,int timeout)和上面类似,不过如果信号量不足,则会消耗 timeout 毫秒的时间来等待信号量足够

 在这些 API 中,release 函数需要了解一下。

QSemaphore sem(5);
sem.acquire(5) // sem.available() = 0
sem.release(5) //sem.available() = 5
sem.release(10) // sem.available() = 15,即多创建了10个资源

通过上述代码可以了解到,如果 release 的值在 sem 的最大值之内,则 sem 的最大值不会变,如果超过了最大值,则会增加新的资源。

总结

本文总结了 Qt 的线程类,以及配套的用于线程安全的互斥锁、条件变量和信号量,相信对大家会有所帮助。

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值