From dc6852ca63ab56eddb8a8843b0c383d51a4020a9 Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Wed, 2 Oct 2013 23:59:17 +0800 Subject: Doc: Expand on thread synchronization details - Introduce the concept of "mutual exclusion" - Rewrite/add explanations on how synchronization happens and how to use these tools - Remove similar content from the "Thread Basics" page - Fix links to examples Change-Id: Id008a8fc3f68bf242cae1704c5c8318149d908b4 Reviewed-by: Olivier Goffart Reviewed-by: Jerome Pasion --- src/corelib/doc/src/threads-basics.qdoc | 26 +++--------- src/corelib/doc/src/threads.qdoc | 74 +++++++++++++++++---------------- 2 files changed, 43 insertions(+), 57 deletions(-) (limited to 'src/corelib/doc') diff --git a/src/corelib/doc/src/threads-basics.qdoc b/src/corelib/doc/src/threads-basics.qdoc index 8b690c15ae..e511c10423 100644 --- a/src/corelib/doc/src/threads-basics.qdoc +++ b/src/corelib/doc/src/threads-basics.qdoc @@ -224,27 +224,11 @@ has terminated. \endlist - \section2 Using a Mutex to Protect the Integrity of Data - - A mutex is an object that has \l{QMutex::}{lock()} and \l{QMutex::}{unlock()} - methods and remembers if it is already locked. A mutex is designed to be - called from multiple threads. \l{QMutex::}{lock()} returns immediately if - the mutex is not locked. The next call from another thread will find the - mutex in a locked state and then \l{QMutex::}{lock()} will block the thread - until the other thread calls \l{QMutex::}{unlock()}. This functionality can - make sure that a code section will be executed by only one thread at a time. - - The following line sketches how a mutex can be used to make a method - thread-safe: - - \code - void Worker::work() - { - this->mutex.lock(); // first thread can pass, other threads will be blocked here - doWork(); - this->mutex.unlock(); - } - \endcode + \section2 Protecting the Integrity of Data + + When writing a multithread application, extra care must be taken to avoid + data corruption. See \l{Synchronizing Threads} for a discussion on how to + use threads safely. \section2 Dealing with Asynchronous Execution diff --git a/src/corelib/doc/src/threads.qdoc b/src/corelib/doc/src/threads.qdoc index 2726ea1709..a1967ff16b 100644 --- a/src/corelib/doc/src/threads.qdoc +++ b/src/corelib/doc/src/threads.qdoc @@ -290,51 +290,46 @@ \contentspage Thread Support in Qt \nextpage Reentrancy and Thread-Safety - While the main idea - with threads is that they should be as concurrent as possible, - there are points where threads must stop and wait for other - threads. For example, if two threads try to access the same - global variable simultaneously, the results are usually - undefined. + While the purpose of threads is to allow code to run in parallel, + there are times where threads must stop and wait for other + threads. For example, if two threads try to write to the same + variable simultaneously, the result is undefined. The principle of + forcing threads to wait for one another is called \e{mutual exclusion}. + It is a common technique for protecting shared resources such as data. Qt provides low-level primitives as well as high-level mechanisms for synchronizing threads. \section1 Low-Level Synchronization Primitives - QMutex provides a mutually exclusive lock, or mutex. At most one - thread can hold the mutex at any time. If a thread tries to - acquire the mutex while the mutex is already locked, the thread will - be put to sleep until the thread that currently holds the mutex - unlocks it. Mutexes are often used to protect accesses to shared - data (i.e., data that can be accessed from multiple threads - simultaneously). In the \l{Reentrancy and Thread-Safety} section - below, we will use it to make a class thread-safe. + QMutex is the basic class for enforcing mutual exclusion. A thread + locks a mutex in order to gain access to a shared resource. If a second + thread tries to lock the mutex while it is already locked, the second + thread will be put to sleep until the first thread completes its task + and unlocks the mutex. QReadWriteLock is similar to QMutex, except that it distinguishes - between "read" and "write" access to shared data and allows - multiple readers to access the data simultaneously. Using - QReadWriteLock instead of QMutex when it is possible can make - multithreaded programs more concurrent. + between "read" and "write" access. When a piece of data is not being + written to, it is safe for multiple threads to read from it simultaneously. + A QMutex forces multiple readers to take turns to read shared data, but a + QReadWriteLock allows simultaneous reading, thus improving parallelism. QSemaphore is a generalization of QMutex that protects a certain - number of identical resources. In contrast, a mutex protects - exactly one resource. The \l{threads/semaphores}{Semaphores} - example shows a typical application of semaphores: synchronizing - access to a circular buffer between a producer and a consumer. - - QWaitCondition allows a thread to wake up other threads when some - condition has been met. One or many threads can block waiting for - a QWaitCondition to set a condition with - \l{QWaitCondition::wakeOne()}{wakeOne()} or - \l{QWaitCondition::wakeAll()}{wakeAll()}. Use - \l{QWaitCondition::wakeOne()}{wakeOne()} to wake one randomly - selected event or \l{QWaitCondition::wakeAll()}{wakeAll()} to - wake them all. The \l{threads/waitconditions}{Wait Conditions} - example shows how to solve the producer-consumer problem using - QWaitCondition instead of QSemaphore. - - Note that Qt's synchronization classes rely on the use of properly + number of identical resources. In contrast, a QMutex protects + exactly one resource. The \l{Semaphores Example} shows a typical application + of semaphores: synchronizing access to a circular buffer between a producer + and a consumer. + + QWaitCondition synchronizes threads not by enforcing mutual exclusion but by + providing a \e{condition variable}. While the other primitives make threads + wait until a resource is unlocked, QWaitCondition makes threads wait until a + particular condition has been met. To allow the waiting threads to proceed, + call \l{QWaitCondition::wakeOne()}{wakeOne()} to wake one randomly + selected thread or \l{QWaitCondition::wakeAll()}{wakeAll()} to wake them all + simultaneously. The \l{Wait Conditions Example} shows how to solve the + producer-consumer problem using QWaitCondition instead of QSemaphore. + + \note Qt's synchronization classes rely on the use of properly aligned pointers. For instance, you cannot use packed classes with MSVC. @@ -381,7 +376,14 @@ 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. + 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 \l{Implicit Sharing}{implicitly + shared} 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. \sa QThread::exec(), {Threads and QObjects} */ -- cgit v1.2.3