Qt之线程同步

简介: 简述使用线程的目的是允许代码并行运行,但是有时线程必须停止并等待其他线程。例如,如果两个线程试图同时写入相同的变量,结果是未知的。 迫使线程等待另一个的原则被称为互斥 。 这是一种保护共享资源等数据的常见的技术。简述低级同步原语风险便利类高级事件队列低级同步原语QMutex 是强制执行互斥的基本类。一个线程锁定一个互斥量(mu

简述

使用线程的目的是允许代码并行运行,但是有时线程必须停止并等待其他线程。例如,如果两个线程试图同时写入相同的变量,结果是未知的。 迫使线程等待另一个的原则被称为互斥 。 这是一种保护共享资源等数据的常见的技术。

低级同步原语

QMutex 是强制执行互斥的基本类。一个线程锁定一个互斥量(mutex),以获得共享资源的访问权限。如果另一个线程试图锁定互斥量,而互斥量已被锁定,这时,它将进入睡眠状态,直到第一个线程完成其任务并解锁互斥量。

QReadWriteLock 类似于 QMutex,除了它区分了“读”和“写”的访问。当一个数据块没有被写入,多线程对他同时进行读取是安全的。一个 QMutex 迫使轮流读取共享数据,而 QReadWriteLock 允许同时读取,从而提高并行性。

QSemaphore 是 QMutex 的一个推广,可以保护一定数量相同的资源。相比之下,一个 QMutex 只能保护一个资源。Qt之线程同步(生产者消费者模式 - QSemaphore) 提供了一个典型的案例,信号量:在“生产者 - 消费者”之间同步访问循环缓冲区。

QWaitCondition 同步线程,不通过执行互斥,而通过提供一个条件变量。其它原语使线程等待,直到资源被解锁;QWaitCondition 使线程等待,直到满足一个特定的条件。让等待中的线程继续进行,调用 wakeOne() 来唤醒一个随机选择的线程或 wakeAll() 同时唤醒所有。Qt之线程同步(生产者消费者模式 - QWaitCondition) 显示了如何使用 QWaitCondition 代替 QSemaphore 来实现“生产者 - 消费者”模式。

这些同步类可以用来使一个方法线程安全。然而,这样做会带来性能损失,这就是为什么大多数 Qt 方法不是线程安全的。

风险

如果一个线程锁定了一个资源,但是没有解锁,应用程序可能会被冻结,因为该资源对于其他线程将永久不可用。这很有可能发生,例如,程序抛出一个异常,并强制当前函数返回而没有释放锁。

另一个类似的场景是死锁 。例如,假设线程 A 正在等待线程 B 释放资源,如果线程 B 也等待线程 A 释放不同的资源,那么着两个线程将会永远等待,因此应用程序将被冻结。

  • 典型案例

哲学家进餐问题:由荷兰学者 Dijkstra 提出的经典的同步问题之一。

这里写图片描述

问题描述:

哲学家进餐问题描述有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,进餐完毕,放下筷子又继续思考。

约束条件:

  1. 只有拿到两只筷子时,哲学家才能吃饭。
  2. 如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。
  3. 任一哲学家在自己未拿到两只筷子吃饭前,不会放下手中拿到的筷子。

便利类

QMutexLocker、QReadLocker 和 QWriteLocker 是便利类,使其更易于使用 QMutex 和QReadWriteLock 。当他们被构建时,就会锁定资源;当被销毁时,就会自动解锁。设计他们是为了简化使用 QMutex 和 QReadWriteLock 的代码,从而减少资源被永久锁定的可能性。

高级事件队列

Qt之事件系统 对于线程间通信非常有用,每个线程可以有自己的事件循环。要在另外一个线程中调用一个槽函数(或任何 invokable 方法),需要将调用放置在目标线程的事件循环中。这让目标线程在槽函数开始运行之前,先完成自己的当前任务,而原来的线程继续并行运行。

要在一个事件循环中执行调用,需要一个 queued 信号槽连接。每当信号发出时,它的参数将被事件系统记录。信号接收者存活的线程将运行槽函数。另外,不使用信号,调用 QMetaObject::invokeMethod() 也可以达到相同的效果。在这两种情况下,必须使用 queued 连接,因为 direct 连接绕过了事件系统,并且立即在当前线程中运行此方法。

当为线程同步使用事件系统时,没有死锁风险,不像使用低级原语 。然而,事件系统不执行互斥。如果调用方法访问共享数据,仍然需要使用低级原语来保护。

话虽如此,Qt 的事件系统,以及 implicitly shared(隐式共享)数据结构,提供了一种替代传统的线程锁定。如果只使用信号槽,并且线程间没有共享变量,那么,多线程程序可以完全没有低级原语。

目录
相关文章
|
1月前
|
安全 Java
Qt经典面试题:Qt开启线程的几种方式
Qt经典面试题:Qt开启线程的几种方式
22 0
|
28天前
|
算法 Unix Linux
Linux与Qt线程优先级的对应关系:一次全面解析
Linux与Qt线程优先级的对应关系:一次全面解析
21 0
|
1月前
|
存储 并行计算 安全
【Qt 线程】探索Qt线程编程的奥秘:多角度深入剖析(二)
【Qt 线程】探索Qt线程编程的奥秘:多角度深入剖析
58 0
|
27天前
|
安全 数据处理 C++
【Qt 底层之事件驱动系统】深入理解 Qt 事件机制:主事件循环与工作线程的交互探究,包括 QML 的视角
【Qt 底层之事件驱动系统】深入理解 Qt 事件机制:主事件循环与工作线程的交互探究,包括 QML 的视角
111 3
|
29天前
|
Java 程序员 API
【深入探究 Qt 线程】一文详细解析Qt线程的内部原理与实现策略
【深入探究 Qt 线程】一文详细解析Qt线程的内部原理与实现策略
76 0
|
1月前
|
Java API C++
【C++ 与Qt 线程】C++ std::thread 与Qt qthread多线程混合编程
【C++ 与Qt 线程】C++ std::thread 与Qt qthread多线程混合编程
45 1
|
1月前
|
安全
深入理解Qt多线程编程:QThread、QTimer与QAudioOutput的内在联系__Qt 事件循环(三)
深入理解Qt多线程编程:QThread、QTimer与QAudioOutput的内在联系__Qt 事件循环
22 0
|
1月前
|
存储 安全 程序员
深入理解Qt多线程编程:QThread、QTimer与QAudioOutput的内在联系__Qt 事件循环(二)
深入理解Qt多线程编程:QThread、QTimer与QAudioOutput的内在联系__Qt 事件循环
41 0
|
1月前
|
安全 测试技术 C++
深入理解Qt多线程编程:QThread、QTimer与QAudioOutput的内在联系__Qt 事件循环(一)
深入理解Qt多线程编程:QThread、QTimer与QAudioOutput的内在联系__Qt 事件循环
41 0
|
1月前
|
负载均衡 并行计算 安全
【Qt 线程】探索Qt线程编程的奥秘:多角度深入剖析(三)
【Qt 线程】探索Qt线程编程的奥秘:多角度深入剖析
44 0