【QT】线程同步

线程同步

尽管线程的目的是允许代码并行运行,但有时线程必须停下来等待其他线程。例如,如果两个线程尝试同时写入同一变量,结果是不确定的。强制线程彼此等待的原则称为互斥。这是一种保护共享资源(如数据)的常见技术。

Qt 提供了低级别的原始机制以及高级别的线程同步机制。

低级别同步原语

QMutex 是实现互斥的基本类。线程锁定互斥体以访问共享资源。如果第二个线程在互斥体已被锁定时尝试锁定它,第二个线程将被挂起,直到第一个线程完成任务并解锁互斥体。

QReadWriteLock 与 QMutex 类似,但它区分“读”和“写”访问。当一段数据不被写入时,多个线程同时从中读取是安全的。QMutex 强制多个读者轮流读取共享数据,而 QReadWriteLock 允许同时读取,从而提高了并行性。

QSemaphore 是保护一定数量相同资源的泛化 QMutex。相比之下,QMutex 保护一个资源。信号量示例展示了信号量的典型应用:在生产者和消费者之间同步访问循环缓冲区。

QWaitCondition 不是通过强制互斥来同步线程,而是提供条件变量。其他原语使线程等待直到资源被解锁,而 QWaitCondition 使线程等待直到特定条件满足。要允许等待的线程继续,调用 wakeOne() 来唤醒一个随机选择的线程或者调用 wakeAll() 来同时唤醒所有线程。等待条件示例展示了如何使用 QWaitCondition 解决生产者-消费者问题,而不是使用 QSemaphore。

注意: Qt 的同步类依赖于正确对齐的指针的使用。例如,您不能在 MSVC 中使用紧凑类。

这些同步类可用于使方法线程安全。然而,这样做会导致性能损失,这就是为什么大多数 Qt 方法不会被设置为线程安全的原因。

风险

如果一个线程锁定资源但不解锁它,应用程序可能会冻结,因为资源将永久不可用于其他线程。例如,如果抛出异常并强制当前函数返回而不释放其锁,就会发生这种情况。

另一个类似的场景是死锁。例如,假设线程 A 正在等待线程 B 解锁一个资源。如果线程 B 也在等待线程 A 解锁另一个资源,那么两个线程最终都将永远等待,因此应用程序将冻结。

便利类

QMutexLocker、QReadLocker 和 QWriteLocker 是使使用 QMutex 和 QReadWriteLock 更加简便的类。它们在构造时锁定资源,并在销毁时自动解锁。它们旨在简化使用 QMutex 和 QReadWriteLock 的代码,从而降低资源意外永久锁定的可能性。

高级事件队列

Qt 的事件系统对于跨线程通信非常有用。每个线程都可以有自己的事件循环。要在另一个线程中调用槽(或任何可调用的方法),请将该调用放在目标线程的事件循环中。这样做可以让目标线程在槽开始运行之前完成当前任务,同时原始线程可以继续并行运行。

要将调用放置在事件循环中,创建一个排队的信号-槽连接。每当信号被发射时,其参数将被事件系统记录。信号接收器所在的线程将运行槽。或者,调用 QMetaObject::invokeMethod() 来实现相同的效果,但需要使用排队连接,因为直接连接将绕过事件系统并立即在当前线程中运行方法。

使用事件系统进行线程同步时不会出现死锁的风险,与使用低级原语不同。然而,事件系统不会强制执行互斥。如果可调用方法访问共享数据,它们仍然必须使用低级原语进行保护。

总的来说,Qt 的事件系统以及隐式共享的数据结构提供了一种替代传统线程锁定的方法。如果仅使用信号和槽,并且没有变量在线程之间共享,多线程程序可以完全不使用低级原语。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值