本页内容

线程同步

虽然线程的目的是让代码并行运行,但在某些情况下,线程必须暂停并等待其他线程。 例如,如果两个线程同时尝试向同一个变量写入数据,结果将无法确定。强制线程相互等待的原则称为互斥。这是保护数据等共享资源的一种常见技术。

Qt 提供了用于线程同步的低级原语以及高级机制。

低级同步原语

QMutex 是实现互斥的基本类。一个线程通过锁定互斥量来获取对共享资源的访问权限。如果第二个线程试图锁定已经处于锁定状态的互斥量,则该线程将被置于休眠状态,直到第一个线程完成任务并解锁互斥量为止。

QReadWriteLock 与QMutex 类似,区别在于它区分“读取”和“写入”访问。当某段数据未被写入时,多个线程可安全地同时从中读取。QMutex 会强制多个读取者轮流读取共享数据,而QReadWriteLock 则允许同时读取,从而提高了并行性。

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

QWaitCondition 它并非通过强制互斥来同步线程,而是通过提供条件变量来实现。其他原语会使线程等待直至资源被解锁,而QWaitCondition 则使线程等待直至满足特定条件。要让等待的线程继续执行,可调用wakeOne()随机唤醒一个线程,或调用wakeAll()同时唤醒所有线程。“使用等待条件的生产者与消费者”示例展示了如何使用QWaitCondition 代替QSemaphore 来解决生产者-消费者问题。

注意:Qt 的 同步类依赖于正确对齐的指针。例如,在 MSVC 环境下不能使用 packed 类。

这些同步类可用于使方法线程安全。但是,这样做会带来性能开销,这就是为什么大多数 Qt 方法并未设计为线程安全的原因。

风险

如果一个线程锁定了资源却未解锁,应用程序可能会冻结,因为该资源将对其他线程永久不可用。例如,当抛出异常并迫使当前函数在未释放锁的情况下返回时,就会发生这种情况。

另一种类似的情况是死锁。例如,假设线程 A 正在等待线程 B 解锁一个资源。如果线程 B 也在等待线程 A 解锁另一个资源,那么这两个线程最终将陷入无限等待,从而导致应用程序冻结。

便利类

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

高级事件队列

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

要将调用放入事件循环中,请建立队列式的信号-槽连接。 每当发出信号时,其参数都会被事件系统记录下来。随后,信号接收器lives in 所属的线程将运行该槽。或者,调用QMetaObject::invokeMethod() 可以在不使用信号的情况下实现相同的效果。这两种情况下都必须使用queued connection ,因为direct connection 会绕过事件系统,并立即在当前线程中运行该方法。

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

话虽如此,Qt 的事件系统配合隐式共享的数据结构,为传统的线程锁定提供了一种替代方案。如果仅使用信号和槽,且线程之间不共享任何变量,则多线程程序可以完全不使用低级原语。

另请参阅 QThread::exec() 以及《线程与 QObject》。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.