同步线程

虽然线程的目的是允许代码并行运行,但有时线程必须停止并等待其他线程。例如,如果两个线程试图同时向同一个变量写入内容,结果将是无法定义的。强制线程相互等待的原则称为互斥。这是一种保护数据等共享资源的常用技术。

Qt 提供了用于同步线程的底层原语和高层机制。

低级同步原语

QMutex 是执行互斥的基本类。一个线程锁定一个互斥体,以获得对共享资源的访问权限。如果第二个线程试图锁定已被锁定的互斥体,第二个线程将进入休眠状态,直到第一个线程完成任务并解锁互斥体。

QReadWriteLock 除了区分 "读取 "和 "写入 "访问外,"读取 "和 "写入 "访问与 类似。当一段数据没有被写入时,多个线程同时读取它是安全的。 迫使多个读取器轮流读取共享数据,但 允许同时读取,从而提高了并行性。QMutex QMutex QReadWriteLock

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

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

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

这些同步类可用于确保方法的线程安全。不过,这样做会带来性能损失,这就是为什么大多数 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() 以及线程和 QObjects

© 2025 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.