Qt-系统线程安全(63)

发布于:2024-10-17 ⋅ 阅读:(13) ⋅ 点赞:(0)

目录

描述

使用

线程不安全

线程安全

释放锁问题

其他的锁

条件变量和信号量


描述

多线程程序太复杂了

在C/C++ 和 Linux中,我们为了保证线程安全,简单的方式就是加锁

 

为此 Qt  也封装了自己的一套锁管理

使用

线程不安全

我们先测验一下线程不安全的情况,先自定义一个类,等会要重写 QThread 类

thread.h 

thread.cpp

一个简单的累加 

运行发现,结果并不是我们所想象中的那样

线程安全

对++ 进行加锁,在CPU指令之中,++操作一共有三个指令,本身不是原子的,所以有线程不安全的问题,我们对涉及到修改的代码块进行加锁,保证线程安全

创建一共静态的锁对象,确保是使用同一把锁

运行正常,累加到了应有的数值 

 

释放锁问题

关于锁的释放,是有可能忘记释放的,忘记 unlock

在临界区之中,由于可能存在 判断,异常之类的操作,可能会导致锁没有释放

 

在C++释放内存中也有这样的问题存在,因此C++ 引入了智能指针,来解决内存释放和锁释放的问题

在出花括号区域的时候,lock_guard会自动调用析构,来释放 锁 

 

Qt 为了锁的释放也参考了这种做法,创建了 mutexLocker

使用如下,和C++之中的 std::lock_guard 使用方式一样

不建议和C++混着用锁相关函数

其他的锁

Qt中还有很多其他的锁,诸如读写锁,在特定的场景有着很好的发挥

QReadWriteLocker、QReadLocker、QWriteLocker

QReadWriteLock 是读写锁类,⽤于控制读和写的并发访问。
QReadLocker ⽤于读操作上锁,允许多个线程同时读取共享资源。
QWriteLocker ⽤于写操作上锁,只允许⼀个线程写⼊共享资源。

条件变量和信号量

条件变量

        在多线程编程中,假设除了等待操作系统正在执⾏的线程之外,某个线程还必须等待某些条件满⾜才能执⾏,这时就会出现问题。这种情况下,线程会很⾃然地使⽤锁的机制来阻塞其他线程,因为这只是线程的轮流使⽤,并且该线程等待某些特定条件,⼈们会认为需要等待条件的线程,在释放互斥锁或读写锁之后进⼊了睡眠状态,这样其他线程就可以继续运⾏。当条件满⾜时,等待条件的线程将被另⼀个线程唤醒。
在 Qt 中,专⻔提供了 QWaitCondition类 来解决像上述这样的问题。
        特点:QWaitCondition 是 Qt 框架提供的条件变量类,⽤于线程之间的消息通信和同步。
        ⽤途:在某个条件满⾜时等待或唤醒线程,⽤于线程的同步和协调

伪代码如下,使用过程也大致如下 

QMutex mutex;
QWaitCondition condition;
//在等待线程中
mutex.lock();
//检查条件是否满⾜,若不满⾜则等待
while (!conditionFullfilled())
{
condition.wait(&mutex); //等待条件满⾜并释放锁
}
//条件满⾜后继续执⾏
//...
mutex.unlock();
//在改变条件的线程中
mutex.lock();
//改变条件
changeCondition();
condition.wakeAll(); //唤醒等待的线程
mutex.unlock();

信号量

        有时在多线程编程中,需要确保多个线程可以相应的访问⼀个数量有限的相同资源。例如,运⾏程序的设备可能是⾮常有限的内存,因此我们更希望需要⼤量内存的线程将这⼀事实考虑在内,并根据可⽤的内存数量进⾏相关操作,多线程编程中类似问题通常⽤信号量来处理。信号量类似于增强的互斥锁,不仅能完成上锁和解锁操作,⽽且可以跟踪可⽤资源的数量。
        特点:QSemaphore 是 Qt 框架提供的计数信号量类,⽤于控制同时访问共享资源的线程数量。
        ⽤途:限制并发线程数量,⽤于解决⼀些资源有限的问题

伪代码,使用过程如下 

QSemaphore semaphore(2); //同时允许两个线程访问共享资源
//在需要访问共享资源的线程中
semaphore.acquire(); //尝试获取信号量,若已满则阻塞
//访问共享资源
//...
semaphore.release(); //释放信号量
//在另⼀个线程中进⾏类似操作