Qt processes and threads: thread synchronization, reentrancy and thread safety

1. Synchronous thread method

The purpose of using threads is to allow code to run in parallel, but sometimes threads must stop and wait for other threads. For example, if two threads try to write to the same variable at the same time, the result is undefined, so the threads need to be synchronized. Synchronizing threads is a common technique for protecting data such as shared resources. The principle of forcing a thread to wait for another is called mutual exclusion.

The QMutex, QReadWriteLock, QSemaphore, and QWaitCondition classes in Qt provide methods for synchronizing threads.

  • QMutex provides a mutual exclusion lock (mutex), and at most one thread can acquire the mutex at any time. If a thread tries to acquire a mutex while the mutex is already locked, the thread will sleep until the thread that now acquires the mutex unlocks the mutex. Mutexes are often used to protect access to shared data (eg, data that can be accessed by multiple threads simultaneously).
  • QReadWriteLock is a read-write lock, which is very similar to QMutex, except that it divides access to shared data into "read" access and "write" access, allowing multiple threads to simultaneously "read" access to data. Using QReadWriteLock instead of QMutex when possible can improve the concurrency of multi-threaded programs.
  • QSemaphore is a semaphore, which is a generalization of QMutex and is used to protect a certain number of the same resources, while a mutex mutex can only protect one resource. Qt's thread synchronization (producer consumer mode - QSemaphore) provides a typical case, semaphore: synchronous access to the circular buffer between "producer-consumer".
  • QWaitCondition is a condition variable that allows a thread to wake up other threads when some conditions are met. One or more threads can be blocked waiting for a QWaitCondition to set a condition for wakeOne() or wakeAll(). Use wakeOneO to wake up a randomly selected waiting thread, and use wakeAll() to wake up all waiting threads.

The benefits of this article, the fee to receive Qt development learning materials package, technical video, including (C++ language foundation, Qt programming introduction, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓ See below

2. Reentrancy and thread safety

The terms "reentrancy" and "thread safety" are used here to label classes and functions to indicate how they are intended to be used in multithreaded applications.

  • A thread-safe function can be called by multiple threads at the same time, and there is no problem even if the caller uses shared data, because access to shared data is serial.
  • A reentrant function can also be called by multiple threads at the same time, but each caller can only use its own data.

Therefore, a thread-safe function is always reentrant, but a reentrant function is not necessarily thread-safe. By extension, a reentrant class means that its member functions can be safely called by multiple threads, as long as each thread uses a different object of this class. A thread-safe class means that its member functions can be safely called by multiple threads, even if all threads use the same instance of the class.

Note: Some classes of Qt are designed to be thread-safe if their purpose is to be multi-threaded. If a function is not marked as thread-safe or reentrant, it should not be used by different threads. If a class is not marked thread-safe or reentrant, instances of that class should not be accessed by multiple threads.

Reentrancy

C++ classes tend to be reentrant simply because they can only access their own data. Any thread can access a member function of a reentrant class instance, as long as no other thread calls the instance's member function at the same time. For example, the following Counter class is reentrant:

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { ++n; }
    void decrement() { --n; }
    int value() const { return n; }

private:
    int n;
};

This class is not thread-safe because if multiple threads attempt to modify data member n, the results are undefined. This is because neither ++ nor -- operations are always atomic. In fact, they generally expand into 3 machine instructions:

  1. load variable value into register
  2. increment or decrement the value in a register
  3. Write the value in the register back to memory

If thread A and thread B load the old value of the variable into a register at the same time, increment the value in the register, and write it back to memory, they will eventually overwrite each other, causing the variable value to be incremented only once!

thread safety

Obviously, the access should be serial: thread A must execute 1.2.3. three steps (atomicity) without interruption, and then thread B can start execution, and vice versa. A simple way to make a class thread-safe is to use a QMutex to protect all access to data members.

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { QMutexLocker locker(&mutex); ++n; }
    void decrement() { QMutexLocker locker(&mutex); --n; }
    int value() const { QMutexLocker locker(&mutex); return n; }

private:
    mutable QMutex mutex;
    int n;
};

The QMutexLocker class automatically locks the mutex in its constructor, and unlocks it when the destructor is called. Locking the mutex ensures that access by other threads will be serialized. The mutex data member is declared mutable because value() is a const function in which we need to lock and unlock the mutex.

Notes on Qt classes

Many Qt classes are reentrant, but not thread-safe, because thread-safety means adding additional overhead for locking and unlocking a QMutex. For example: QString is reentrant but not thread safe. You can access different instances of QString from multiple threads at the same time, but you cannot access the same instance of QString from multiple threads at the same time (unless you use QMutex to protect access).

Some Qt classes and functions are thread-safe. They are mainly thread-related classes (eg: QMutex) and some basic functions (eg: QCoreApplication::postEvent()).

The article is transferred from blog garden (fengMisaka): Qt process and thread three: thread synchronization, reentrancy and thread safety - fengMisaka - blog garden

The benefits of this article, the fee to receive Qt development learning materials package, technical video, including (C++ language foundation, Qt programming introduction, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓ See below

Guess you like

Origin blog.csdn.net/QtCompany/article/details/131871929