雖然綫程的目的是允許代碼並行運行,但有時綫程必須停止並等待其它綫程。例如:若 2 綫程試著同時寫入同一變量,結果就是未定義的。強製綫程互相等待的原理稱為 相互排斥 。它是保護共享資源 (如:數據) 的常見技術。
Qt 為同步綫程提供瞭低級原語及高級機製。
QMutex 是實施相互排斥的基礎類。綫程鎖定互斥以獲得對共享資源的訪問。若第 2 綫程試著鎖定已鎖定的互斥,第 2 綫程將進入休眠狀態,直到第 1 綫程完成其任務並解鎖互斥。
QReadWriteLock 類似於 QMutex ,除區分 Read (讀取) 和 Write (寫入) 訪問外。當不寫入數據塊時,多個綫程同時讀取是安全的。 QMutex 強製多個讀取器輪流讀取共享數據,但 QReadWriteLock 允許同時讀取,從而改善並行性。
QSemaphore 是一般化的 QMutex ,保護一定數量的恒等資源。相比之下, QMutex 準確保護某一資源。 信號量範例 展示信號量的典型應用程序:在生産者和消費者之間同步訪問循環緩衝。
QWaitCondition 同步綫程不是通過強製互斥排除,而是通過提供 條件變量 。當其它原語使綫程等待,直到解鎖資源時, QWaitCondition makes threads wait until a particular condition has been met. To allow the waiting threads to proceed, call wakeOne() to wake one randomly selected thread or wakeAll() to wake them all simultaneously. The 等待條件範例 shows how to solve the producer-consumer problem using QWaitCondition 而不是 QSemaphore .
注意: Qt's synchronization classes rely on the use of properly aligned pointers. For instance, you cannot use packed classes with MSVC.
These synchronization classes can be used to make a method thread safe. However, doing so incurs a performance penalty, which is why most Qt methods are not made thread safe.
If a thread locks a resource but does not unlock it, the application may freeze because the resource will become permanently unavailable to other threads. This can happen, for example, if an exception is thrown and forces the current function to return without releasing its lock.
另一類似情景是 deadlock . For example, suppose that thread A is waiting for thread B to unlock a resource. If thread B is also waiting for thread A to unlock a different resource, then both threads will end up waiting forever, so the application will freeze.
QMutexLocker , QReadLocker and QWriteLocker 是方便類使之更易於使用 QMutex and QReadWriteLock . They lock a resource when they are constructed, and automatically unlock it when they are destroyed. They are designed to simplify code that use QMutex and QReadWriteLock , thus reducing the chances that a resource becomes permanently locked by accident.
Qt 的 事件係統 is very useful for inter-thread communication. Every thread may have its own event loop. To call a slot (or any invokable method) in another thread, place that call in the target thread's event loop. This lets the target thread finish its current task before the slot starts running, while the original thread continues running in parallel.
To place an invocation in an event loop, make a queued 信號-槽 connection. Whenever the signal is emitted, its arguments will be recorded by the event system. The thread that the signal receiver 活在 will then run the slot. Alternatively, call QMetaObject::invokeMethod () to achieve the same effect without signals. In both cases, a 隊列連接 must be used because a 直接連接 bypasses the event system and runs the method immediately in the current thread.
There is no risk of deadlocks when using the event system for thread synchronization, unlike using low-level primitives. However, the event system does not enforce mutual exclusion. If invokable methods access shared data, they must still be protected with low-level primitives.
Having said that, Qt's event system, along with 隱式共享 data structures, offers an alternative to traditional thread locking. If signals and slots are used exclusively and no variables are shared between threads, a multithreaded program can do without low-level primitives altogether.
另請參閱 QThread::exec () 和 綫程和 QObject .