Qt之线程同步

简述

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

低级同步原语

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(隐式共享)数据结构,提供了一种替代传统的线程锁定。如果只使用信号槽,并且线程间没有共享变量,那么,多线程程序可以完全没有低级原语。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值