What is the role of QT multi-threaded programming

1. Thread basics

1. GUI thread and worker thread

The first thread that each program has after it starts is called the main thread, which is the GUI thread. All component classes and several related classes in QT can only work on the GUI thread, not the secondary thread. The secondary thread is the working thread, which is mainly responsible for the unloading of the GUI thread.

2. Synchronous access to data

Each thread has its own stack, so each thread has its own call history and local variables. Threads share the same address space.

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

2. Introduction to QT multithreading

QT provides support for threads in three forms, namely, platform-independent thread classes, thread-safe event delivery, and cross-thread signal-slot connections.
Thread classes in QT include the following:

  • QThread provides a cross-platform multithreading solution
  • QThreadStorage provides per-thread data storage
  • QMutex provides a mutually exclusive lock, or mutex
  • QMutexLocker is an auxiliary class that automatically locks and unlocks QMutex
  • QReadWriterLock provides a lock for simultaneous read operations
  • QReadLocker and QWriteLocker automatically lock and unlock QReadWriteLock
  • QSemaphore provides an integer semaphore, which is a generalization of mutex
  • QWaitCondition provides a way for a thread to sleep until it is woken up by another thread.

Three, QThread thread

1. QThread thread basics

QThread is a common abstract class in Qt thread. All thread classes are derived from the QThread abstract class. It is necessary to implement the virtual function run() in QThread, and call the run function through the start() function.
The void run() function is a thread body function that defines the functionality of the thread.
The void start() function is a startup function, which is used to set the thread entry address to the run function.
The void terminate() function is used to forcibly end the thread without guaranteeing data integrity and resource release.
QCoreApplication::exec() is always called in the main thread (the thread that executes main()), and cannot be called from a QThread. In a GUI program, the main thread, also known as the GUI thread, is the only thread that is allowed to perform GUI-related operations. In addition, a QApplication (or QCoreApplication) object must be created before creating a QThread.
When the thread starts and ends, QThread will send the signals started() and finished(), and you can use isFinished() and isRunning() to query the status of the thread.
From Qt4.8 onwards, it is possible to release a thread object that has just finished running, by connecting the finished() signal to the QObject::deleteLater() slot.
Use wait() to block the calling thread until other threads have finished executing (or until the specified time elapses).
The static functions currentThreadId() and currentThread() return the ID of the currently executing thread. The former returns the ID of the thread, and the latter returns a thread pointer.
To set the thread's name, call setObjectName() before starting the thread. If setObjectName() is not called, the thread's name will be the runtime type of the thread object (the class name of the QThread subclass).

2. Thread priority

QThread threads have a total of 8 priorities

QThread::IdlePriority   0 scheduled only when no other threads are running.
    QThread::LowestPriority  1 scheduled less often than LowPriority.
    QThread::LowPriority   2 scheduled less often than NormalPriority.
    QThread::NormalPriority  3 the default priority of the operating system.
    QThread::HighPriority   4 scheduled more often than NormalPriority.
    QThread::HighestPriority  5 scheduled more often than HighPriority.
    QThread::TimeCriticalPriority 6 scheduled as often as possible.
    QThread::InheritPriority   7 use the same priority as the creating thread. This is the default.
    void setPriority(Priority priority)

Set the priority of the running thread. If the thread is not running, this function does nothing and returns immediately. Use start() to start a thread with a specific priority. The priority parameter can be any value of the QThread::Priority enumeration except InheritPriortyd.

3. Thread creation

 void start ( Priority priority = InheritPriority )

Start the thread execution, and the started () signal will be sent after startup

4. Thread execution

int exec() [protected]

Enter the event loop and wait until exit() is called. The return value is obtained by calling exit(), and returns 0 if the call is successful.

void run() [virtual protected]

The starting point of the thread. After calling start(), the newly created thread will call the run function. The default implementation calls exec(). Most of them need to reimplement the run function to manage their own threads. When the run function returns, the execution of the thread will end.

5. Thread exit

void quit();

Notify the thread event loop to exit, and return 0 to indicate success, which is equivalent to calling QThread::exit(0).

void exit ( int returnCode = 0 );

After calling exit, the thread will exit the event loop and return from exec, and the return value of exec is returnCode. Usually returnCode=0 means success, other values ​​mean failure.

void terminate ();

End thread, whether the thread terminates immediately depends on the operating system.

When the thread is terminated, all threads waiting for the thread Finished will be woken up.

Whether terminate is called depends on the setTerminationEnabled ( bool enabled = true ) switch.

void requestInterruption()

Interruption of the requested thread. Requests are advisory and it is up to the code running on the thread to decide whether and how to implement such a request. This function does not stop any event loop running on the thread, and does not terminate it under any circumstances.

The solution for thread exit in the project is as follows:

By adding the identification variable volatile bool m_stop in the thread class, the value of the m_stop variable can be used to determine whether the run function returns after execution.

#ifndef WORKTHREAD_H
#define WORKTHREAD_H
#include <QThread>
#include <QDebug>
class WorkThread : public QThread
{
protected:
  //线程退出的标识量
  volatile bool m_stop;
  void run()
  {
    qDebug() << "run begin";
    while(!m_stop)
    {
        //task handling
        int* p = new int[1000];
        for(int i = 0; i < 1000; i++)
        {
            p[i] = i * i;
        }
        sleep(2);
        delete [] p;
    }
    qDebug() << "run end";
  }
public:
  WorkThread()
    m_stop = false;
  //线程退出的接口函数,用户使用
  void stop()
    m_stop = true;
};
#endif // WORKTHREAD_H

6. Thread waiting

bool wait ( unsigned long time = ULONG_MAX )

The thread will be blocked, waiting for time milliseconds, if the thread exits, wait will return. The Wait function solves the dependence of multithreading on execution timing.

void msleep ( unsigned long msecs )
void sleep ( unsigned long secs )
void usleep ( unsigned long usecs )

sleep(), msleep(), usleep() allow seconds, milliseconds and microseconds to be distinguished, but are set to public in Qt5.0.

In general, the wait() and sleep() functions should not be needed, because Qt is an event-driven framework. Consider listening to the finished() signal instead of wait() and using a QTimer instead of sleep().

7. The state of the thread

bool isFinished () const thread has exited
bool isRunning () const thread is running

8. Properties of threads

Priority priority () const
void setPriority ( Priority priority )
uint stackSize () const
void setStackSize ( uint stackSize )
void setTerminationEnabled ( bool enabled = true )

Set whether to respond to the terminate() function

9. Thread and event loop

The default implementation of run() in QThread calls exec() to create a QEventLoop object, and the QEventLoop object processes events in the event queue in the thread (each thread has its own event queue). exec() is constantly doing the work of looping through the event queue internally, calling the quit() or exit() method of QThread to exit the thread, try not to use terminate() to exit the thread, terminate() is too rough to exit the thread, causing resource It cannot be released, even the mutex is still locked.

The event loop in the thread allows the thread to use those non-GUI classes that require an event loop (eg, QTimer, QTcpSocket, QProcess).

For objects created before QApplication, QObject::thread() returns NULL, which means that the main thread only handles delivery events for these objects, and does not handle additional events for objects that do not have a thread to which they belong. You can use QObject::moveToThread() to change the thread affinity of an object and its children. If the object has a parent, this relationship cannot be moved. It is not safe to delete a QObject object in another thread (other than the thread that created it). Unless it can be guaranteed that the object is not processing events at the same time. You can use QObject::deleteLater(), which will deliver a DeferredDelete event, which will be finally selected by the event loop of the object thread. If no event loop is running, events are not dispatched to objects. If a QTimer object is created in a thread, but exec() is never called, then QTimer will not emit its timeout() signal, and deleteLater() will not work. You can manually use the thread-safe function QCoreApplication::postEvent() to post an event to any object in any thread at any time, and the event will be dispatched through the event loop in the thread that created the object. The event filter is also supported in all threads, but it limits the monitored object and the monitored object to live in the same thread. QCoreApplication::sendEvent (not postEvent()), is only used to post events to the target object in the thread that calls this function.

Fourth, thread synchronization

1. The basics of thread synchronization

Critical resources: resources that are only allowed to be accessed by one thread
at a time Inter-thread mutual exclusion: multiple threads need to access critical resources at the same time.
Thread locks can ensure the security of critical resources. Usually, each critical resource requires a thread lock to access Protect.
Thread deadlock: Threads wait for each other for critical resources so that each other cannot continue to execute.
Conditions for deadlock:
A. There are multiple critical resources in the system and the critical resources cannot be preempted.
B. Threads need multiple critical resources to continue execution.
Deadlock avoidance:
A. Assign a unique resource to each critical resource used. Serial number
B. Allocate the corresponding serial number to the thread lock corresponding to each critical resource
C. Each thread in the system requests critical resources in strict increasing order
QMutex, QReadWriteLock, QSemaphore, QWaitCondition provide thread synchronization means. The main idea of ​​using threads is to hope that they can be executed as concurrently as possible, and threads need to stop or wait between some key points. For example, if two threads try to access the same global variable at the same time, the results may not be as expected.

2. Mutex QMutex

QMutex provides a mutually exclusive lock, or mutex. At most one thread owns a mutex at a time. If a thread tries to access a locked mutex, the thread will sleep until the thread that owns the mutex unlocks the mutex. QMutex is commonly used to protect access to shared data. QMutex class so member functions are thread-safe.

Header file declaration: #include <QMutex>
Mutex declaration: QMutex m_Mutex;
Mutex lock: m_Mutex.lock();
Mutex unlock: m_Mutex.unlock();

If an unlocked mutex is unlocked, the results are undefined. Mutex locking and unlocking must occur in pairs in the same thread.

QMutex ( RecursionMode mode = NonRecursive )

QMutex has two modes: Recursive, NonRecursive

A. Recursive
A thread can lock the mutex multiple times, and the mutex is not actually unlocked until the corresponding number of unlock calls.

B. NonRecursive
default mode, mutex can only be locked once.
If Mutex.lock() is used without the corresponding use of Mutex.unlock(), it will cause a deadlock, and other threads will never get the opportunity to access the shared resources locked by Mutex. Although you can use tryLock(timeout) instead of lock() to avoid deadlocks caused by dead waiting (tryLock(negative value)==lock()), it is still very likely to cause errors.

bool tryLock();
If other threads have already locked the mutex, the call will return immediately without being blocked.
bool tryLock(int timeout);
If other threads currently have locked the mutex, the call will wait for a period of time until timeout

QMutex mutex;
int complexFunction(int flag)
 {
     mutex.lock();
     int retVal = 0;
     switch (flag) {
     case 0:
     case 1:
         mutex.unlock();
         return moreComplexFunction(flag);
     case 2:
         {
             int status = anotherFunction();
             if (status < 0) {
                 mutex.unlock();
                 return -2;
             }
             retVal = status + flag;
         }
         break;
     default:
         if (flag > 10) {
             mutex.unlock();
             return -1;
     }
     mutex.unlock();
     return retVal;
 }

3. Mutex QMutexLocker

In more complex functions and exception handling, it will be very complicated to perform lock() and unlock() operations on mutex objects of QMutex class. Lock() is required at the entry point, and unlock() is required at all exit points. It is easy to appear in a certain Some exit points did not call unlock(), so Qt introduced the auxiliary class QMutexLocker of QMutex to avoid lock() and unlock() operations. Create a QMutexLocker object where the function needs it, and pass the mutex pointer to the QMutexLocker object. At this time, the mutex has been locked. After exiting the function, the local variable of the QMutexLocker object will be destroyed by itself, and the mutex will be unlocked at this time.
Header file declaration: #include<QMutexLocker>
Mutex lock declaration: QMutexLocker mutexLocker(&m_Mutex);
Mutex locking: starting from the declaration (locking in the constructor)
Mutex unlocking: Out of scope automatic unlocking ( unlocked in the destructor)

 int complexFunction(int flag)
     QMutexLocker locker(&mutex);
             if (status < 0)
         if (flag > 10)

4、QReadWriteLock

QReadWriterLock is similar to QMutex, but treats read and write operation access differently, allowing multiple readers to read data at the same time, but only one write, and write and read operations are not performed simultaneously. Using QReadWriteLock instead of QMutex can make multi-threaded programs more concurrent. The default mode of QReadWriterLock is NonRecursive.

The QReadWriterLock class member functions are as follows:

QReadWriteLock ( )
QReadWriteLock ( RecursionMode recursionMode )
void lockForRead ()
void lockForWrite ()
bool tryLockForRead ()
bool tryLockForRead ( int timeout )
bool tryLockForWrite ()
bool tryLockForWrite ( int timeout )
boid unlock ()
使用实例:
QReadWriteLock lock;
 void ReaderThread::run()
     lock.lockForRead();
     read_file();
     lock.unlock();
 void WriterThread::run()
     lock.lockForWrite();
     write_file();

5. QReadLocker and QWriteLocker

In more complex functions and exception handling, it will be very complicated to perform lockForRead()/lockForWrite() and unlock() operations on the QReadWriterLock class lock object. LockForRead()/lockForWrite() is required at the entry point, and unlock is required at all exit points. (), it is easy to appear that unlock() is not called at some jumping points, so Qt introduces the QReadLocker and QWriteLocker classes to simplify the unlocking operation. Create a QReadLocker or QWriteLocker object where the function needs it, and pass the lock pointer to the QReadLocker or QWriteLocker object. At this time, the lock has been locked. After exiting the function, the local variable of the QReadLocker or QWriteLocker object will be destroyed by itself, and the lock will be unlocked at this time.

 QByteArray readData()
     ...
     return data;

Use QReadLocker:

 QReadLocker locker(&lock);

6. Semaphore QSemaphore

QSemaphore is a generalization of QMutex. It is a special thread lock that allows multiple threads to access critical resources at the same time, while a QMutex only protects one critical resource. All member functions of the QSemaphore class are thread-safe.

The classic producer-consumer model is as follows: a factory has only fixed positions, the production personnel produce different products every day, and the sales personnel sell different products every day. When the production personnel produce P products, they need P positions at a time. When the sales personnel sell C products, they require enough products in the warehouse to sell. If there are no P positions in the remaining positions, the products of this batch will not be deposited. When there are no C units of existing products, no more than C products can be sold until new products are added.

QSemaphore to control access to the ring buffer, which is shared by producer threads and consumer threads. The producer keeps writing data to the buffer until the end of the buffer, and then starts from the beginning. Consumers are constantly reading data from the buffer. The semaphore has better concurrency than the mutex. If we use the mutex to control the access to the buffer, then the producer and the consumer cannot access the buffer at the same time. However, we know that there is no harm in having different threads accessing different parts of the buffer at the same time.

QSemaphore class member function:

QSemaphore ( int n = 0 )
void acquire ( int n = 1 )
int available () const
void release ( int n = 1 )
bool tryAcquire ( int n = 1 )
bool tryAcquire ( int n, int timeout )

Example code:

 QSemaphore sem(5);      // sem.available() == 5
 sem.acquire(3);         // sem.available() == 2
 sem.acquire(2);         // sem.available() == 0
 sem.release(5);         // sem.available() == 5
 sem.release(5);         // sem.available() == 10
 sem.tryAcquire(1);      // sem.available() == 9, returns true
 sem.tryAcquire(250);    // sem.available() == 9, returns false

Producer-Consumer instance:

#include <QtCore/QCoreApplication>
#include <QSemaphore>
#include <cstdlib>
#include <cstdio>
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QSemaphore  production(BufferSize);
QSemaphore  consumption;
class Producor:public QThread
    void run();
void Producor::run()
    for(int i = 0; i < DataSize; i++)
        production.acquire();
        buffer[i%BufferSize] = "ACGT"[(int)qrand()%4];
        consumption.release();
}
class Consumer:public QThread
void Consumer::run()
        consumption.acquire();
        fprintf(stderr, "%c", buffer[i%BufferSize]);
        production.release();
    fprintf(stderr, "%c", "\n");
int main(int argc, char *argv[])
    QCoreApplication a(argc, argv);
    Producor productor;
    Consumer consumer;
    productor.start();
    consumer.start();
    productor.wait();
    consumer.wait();
    return a.exec();

Producer::run function:
When the producer thread executes the run function, if the buffer is full and the consumer thread does not read, the producer can no longer write characters to the buffer, and block at producer.acquire until the consumer thread reads (consume) data. Once the producer gets a byte (resource), it writes a random character, and calls consumer.release so that the consumer thread can get a resource (read a byte of data).

Consumer::run function:
When the consumer thread executes the run function, if there is no data in the buffer, the consumer thread blocks at consumer.acquire until the producer thread performs a write operation to write a byte, and executes consumer.release to make the consumer thread When the number of available resources = 1, the consumer thread exits from the blocked state, and the number of consumer resources is -1, and the current number of resources of the consumer = 0.

7. Waiting condition QWaitCondition

QWaitCondition allows threads to wake up other threads when certain conditions occur. One or more threads can block waiting for QWaitCondition, set a condition with wakeOne() or wakeAll(). wakeOne() wakes up one at random, and wakeAll() wakes up all.

QWaitCondition ()
bool wait ( QMutex * mutex, unsigned long time = ULONG_MAX )
bool wait ( QReadWriteLock * readWriteLock, unsigned long time = ULONG_MAX )
void wakeOne ()
void wakeAll ()

Header file declaration: #include <QWaitCondition>
Wait condition declaration: QWaitCondtion m_WaitCondition;
wait condition wait: m_WaitConditon.wait(&m_muxtex, time);
wait condition wakeup: m_WaitCondition.wakeAll();

In a classic producer-consumer scenario, the producer must first check whether the buffer is full (numUsedBytes==BufferSize), and if the buffer is full, the thread stops and waits for the bufferNotFull condition. If not full, generate data in the buffer, increase numUsedBytes, activate condition bufferNotEmpty. Use a mutex to protect access to numUsedBytes. QWaitCondition::wait() receives a mutex as a parameter, and the mutex is initialized to a locked state by the calling thread. Before the thread goes to sleep, the mutex is unlocked. When the thread is woken up, the mutex will be in the locked state, and the transition from the locked state to the waiting state is an atomic operation. When the program starts running, only the producer can work, and the consumer is blocked waiting for the bufferNotEmpty condition. Once the producer puts a byte in the buffer, the bufferNotEmpty condition is fired, and the consumer thread is awakened.

#include <QWaitCondition>
#include <QMutex>
#include <QTime>
const int DataSize = 32;
const int BufferSize = 16;
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
int used = 0;
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
        mutex.lock();
        if(used == BufferSize)
            bufferNotFull.wait(&mutex);
        mutex.unlock();
        buffer[i%BufferSize] = used;
        used++;
        bufferNotEmpty.wakeAll();
        if(used == 0)
            bufferNotEmpty.wait(&mutex);
        fprintf(stderr, "%d\n", buffer[i%BufferSize]);
        used--;
        bufferNotFull.wakeAll();

8. Advanced event queue

The QT event system is very important for inter-process communication. Each process can have its own event loop. To call a slot function (or any invokable method) in another thread, you need to place the call slot function in the event loop of the target thread. , let the target thread complete its current task before the slot function starts running, while the original thread continues to run in parallel.

To execute call-slot functions in an event loop, a queued signal-slot connection is required. Whenever a signal is emitted, the signal's arguments will be logged by the event system. The alive thread of the signal receiver will run the slot function. In addition, without using signals, calling QMetaObject::invokeMethod() can also achieve the same effect. In both cases, a queued connection must be used because a direct connection bypasses the event system and immediately runs this method in the current thread.

When threads use the event system synchronously, there is no risk of deadlock. However, the event system does not enforce mutual exclusion. If the calling method accesses shared data, it still needs to be protected by QMutex.

If only signals and slots are used, and there are no shared variables between threads, then a multithreaded program can have no low-level primitives at all.

5. Reentrancy and thread safety

Reentrant and thread-safe are used to illustrate how a function can be used in multi-threaded programs.
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.
A reentrant class means that the member functions of the class can be safely called by multiple threads, as long as each thread uses a different object of the class. And a thread-safe class means that the member functions of the class can be safely called by multiple threads, even if all threads use the same instance of the class.

1. Reentrant

Most C++ classes are reentrant because they typically only reference member data. Any thread can access the member functions of the reentrant class instance, as long as no other thread calls the member functions of this instance at the same time.

class Counter
  public:
      Counter() {n=0;}
      void increment() {++n;}
      void decrement() {--n;}
      int value() const {return n;}
 private:
      int n;

The Counter class is reentrant, but not thread-safe. If multiple threads attempt to modify data member n, the results are undefined.
Most Qt classes are reentrant and not thread safe. Some classes and functions are thread-safe, mainly thread-related classes, such as QMutex, QCoreApplication::postEvent().

2. Thread safety

All GUI classes (such as QWidget and its subclasses), operating system core classes (such as QProcess) and network classes are not thread-safe.

 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 Counter class is reentrant and thread safe. The QMutexLocker class automatically locks the mutex in the constructor and unlocks it in the destructor. The mutex is modified with the mutable keyword, because the mutex is locked and unlocked in the value() function, and the value() is a const function.

6. Threads and signal slots

1. Thread dependency

Thread dependency is the relationship between an object and a thread. By default, objects are attached to the thread in which they were created.
The relationship between the attachment of the object and the execution of the slot function. By default, the slot function is called and executed in the thread it is attached to.
The method of modifying the dependency of the object: the QObject::moveToThread function is used to change the thread dependency of the object, so that the slot function of the object is called and executed in the attached thread.

2. QObject and thread

The QThread class has the ability to send signals and define slot functions. The main signals of QThread are as follows:

  • void started(); Send a signal when the thread starts running
  • void finished(); Send a signal when the thread finishes running
  • void terminated(); Send a signal when the thread is abnormally terminated

QThread inherits from QObject, emits signals to indicate the start and end of thread execution, and provides many slot functions. QObjects can be used with multiple threads, emitting signals to call slots in other threads, and sending events to objects that "live" in other threads.

Reentrancy of QObject

QObject is reentrant, and most non-GUI subclasses of QObject such as QTimer, QTcpSocket, QUdpSocket, QHttp, QFtp, QProcess are also reentrant, and it is possible to use these classes in multiple threads at the same time. Reentrant classes are designed to be created and used in a single thread, creating an object in one thread and calling the object's functions in another thread is not guaranteed to work. There are three constraints to be aware of:

A. A child of type QObject must always be created in the same thread as its parent. This means, among other things, that the QThread object (this) should never be the parent of an object created in that thread (because the QThread object itself was created in another thread).
B. Event-driven objects may only be used in a single thread. Especially useful for timer mechanisms and networking modules. For example: you cannot start a timer or connect a socket in a thread that does not belong to this object, you must ensure that all objects created in this thread are deleted before deleting QThread. This can be easily done by creating these objects on the stack in the implementation of the run() function.
C. Although QObject is reentrant, GUI classes, especially QWidget and all its subclasses, are not reentrant and can only be used in the GUI thread. QCoreApplication::exec() must also be called from the GUI thread.

In practice, classes that can only use the GUI in the main thread and not in other threads can be easily solved: put the time-consuming operation in a separate worker thread, and when the worker thread is finished, it will be displayed by the screen in the GUI thread. Show results.

In general, creating a QObject before a QApplication is not acceptable and will cause weird crashes or exits, depending on the platform. Therefore, static instances of QObject are not supported. A single-threaded or multi-threaded application should create the QApplication first, and destroy the QObject last.

3. Thread event loop

Each thread has its own event loop. The main thread starts its own event loop through QCoreApplication::exec(), but the GUI application of the dialog box sometimes uses QDialog::exec(), and other threads can use QThread::exec() to start the event loop. Like QCoreApplication, QThread provides an exit(int) function and quit() slot function.

The event loop in the thread allows the thread to use some non-GUI Qt classes that require the existence of an event loop (for example: QTimer, QTcpSocket, and QProcess), making it possible to connect some thread signals to a thread-specific slot function.

A QObject instance is said to live on the thread in which it was created. Events about this object are dispatched to the thread's event loop. You can use the QObject::thread() method to get the thread in which a QObject is located.

The QObject::moveToThread() function changes the threading attribute of an object and its children. (Objects cannot be moved to other threads if they have a parent).

It is not safe to call the delete method on this QObject object from another thread (not the thread to which the QObject object belongs), unless it is guaranteed that the object does not process events at that time. It is better to use QObejct::deleteLater(). An event of type DeferredDelete will be posted, and the event loop of the object's thread will eventually handle the event. By default, the thread that owns a QObject is the thread that created the QObject, not the one after QObject::moveToThread() was called.

If no event loop is running, events will not be delivered to the object. For example: a QTimer object is created in a thread, but exec() is never called, then QTimer will never emit the timeout() signal, even if deleteLater() is called. (These restrictions also apply to the main thread).

Using the thread-safe method QCoreApplication::postEvent(), events can be sent to any object in any thread at any time, and the event will be automatically distributed to the thread event loop where the object was created.

All threads support event filters, with the restriction that the monitor object must exist in the same thread as the monitored object. QCoreApplication::sendEvent() (unlike postEvent()) can only dispatch events to objects in the same thread as the function caller.

4. Other threads access QObject subclasses

QObject and all its subclasses are not thread safe. This encompasses the entire event delivery system. It is important to remember that the event loop may be sending events to your QObject subclass when you access that object from another thread.
If you are calling a function of a QObject subclass, and the subclass object does not live in the current thread, and the object can receive events, then you must protect all internal data of the QObject subclass with a mutex. access, otherwise, crashes and unexpected behavior may occur.
Like other objects, a QThread object lives in the thread it was created in - not the thread it was in when QThread::run() was called. In general, it is not safe to provide slots in a QThread subclass unless the member variable is protected with a mutex.
On the other hand, signals can be emitted safely in the implementation of QThread::run(), because signal emission is thread-safe.

5. Signal slots across threads

The signal slot mechanism of the thread needs to open the event loop mechanism of the thread, that is, call the QThread::exec() function to open the event loop of the thread.
The Qt signal-slot connection function prototype is as follows:

bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection )

Qt supports 5 connection methods

A. Qt::DirectConnection (direct connection mode) (the relationship between the signal and the slot function is similar to a function call, which is executed synchronously).
When the signal is sent, the corresponding slot function will be called immediately. The code after the emit statement will be executed after all slot functions are executed.
The slot function will be called directly when the signal is emitted.
Regardless of which thread the object of the slot function is in, the slot function is executed in the thread that emitted the signal.
B. Qt::QueuedConnection (queue mode) (the signal is stuffed into the event queue at this time, the relationship between the signal and the slot function is similar to message communication, asynchronous execution) when the signal is sent, it is queued
into the signal queue, and it needs to wait until the receiving object belongs to The signal is obtained when the thread's event loop obtains control, and the corresponding slot function is called. The code after the emit statement will be executed immediately after the signal is sent, without waiting for the slot function to finish executing.
The slot function is called when control returns to the event loop of the thread the receiver is attached to.
The slot function executes in the thread the receiver is attached to.
C. Qt::AutoConnection (automatic mode)
Qt's default connection mode, if the signal sender and the object receiving the signal belong to the same thread, the working mode is the same as the direct connection mode; otherwise, the working mode is the same as the queue mode.
If the signal is emitted in the thread to which the receiver is attached, it is equivalent to a direct connection
If the thread that emits the signal is different from the thread to which the receiver is attached, it is equivalent to a queue connection
D, Qt::BlockingQueuedConnection (signals and slots must be in different thread, otherwise a deadlock will occur)
the calling situation of the slot function is the same as that of Queued Connection, the difference is that the current thread will be blocked until the slot function returns.
E. Qt::UniqueConnection
It works the same way as the default, except that the same signal and slot cannot be connected repeatedly, because if repeated connection will cause a signal to be issued, the corresponding slot function will be executed multiple times.

QThread is used to manage threads, and the thread to which the QThread object is attached is not the same concept as the thread it manages. The thread attached to QThread is the thread that creates the QThread object, and the thread managed by QThread is the thread started by run, that is, the newly created thread. The QThread object is attached to the main thread, and the slot function of the QThread object will be executed in the main thread instead of the secondary thread. Unless the QThread object is attached to the secondary thread (via movetoThread).
In engineering practice, in order to avoid freezing the event loop of the main thread (that is, to avoid freezing the UI of the application), all computing work is done in a separate worker thread, and a signal is emitted when the worker thread ends. The parameter sends the state of the worker thread to the slot function of the GUI thread to update the state of the GUI component.

Seven, thread design

1. Thread life cycle

If the thread is being executed and the thread object is destroyed, the program will go wrong.
In engineering practice, the lifetime of the thread object must be longer than the lifetime of the thread.

2. Synchronous thread class design

The thread object actively waits for the end of the thread lifetime before it is destroyed. When the thread object is destroyed, the execution of the thread is guaranteed to end. It supports the creation of thread objects on the stack or heap.
In the destructor of the thread class, the wait function is first called to forcibly wait for the end of thread execution.
Occasions of use: suitable for occasions where the thread life span is short

#ifndef SYNCTHREAD_H
#define SYNCTHREAD_H
class SyncThread : public QThread
  Q_OBJECT
  explicit SyncThread(QObject* parent = 0):QThread(parent)
  ~SyncThread()
    wait();
#endif // SYNCTHREAD_H

3. Asynchronous thread class design

When the thread lifetime ends, the thread object is notified to be destroyed.
Thread objects can only be created in the heap space, and thread objects cannot be actively destroyed by the outside world.
The deleteLater() function is finally called in the run function.
The thread function actively applies for destroying the thread object.
Use occasions:
thread lifetime is uncontrollable, and threads that need to run in the background for a long time.

#ifndef ASYNCTHREAD_H
#define ASYNCTHREAD_H
class AsyncThread : public QThread
    deleteLater();
  explicit AsyncThread(QObject* parent = 0):QThread(parent)
  ~AsyncThread()
  static AsyncThread* newThread(QObject* parent = 0)
    return new AsyncThread(parent);
#endif // ASYNCTHREAD_H

Eight, the use of threads

1. Subclassing QThread

Two ways to use QThread:
(1) Do not use event loop
A, subclass QThread
B, rewrite the run function, there is a while or for infinite loop
C in the run function, set a flag to control the exit of the infinite loop .
It is suitable for performing long-term and time-consuming operations in the background, such as file copying and network data reading.
(2) Use the event loop.
A. Subclassing QThread
B. Rewriting run to call QThread::exec() to open the event loop of the thread
C. Define signals and slots for the subclass. Since the slot function will not run in the newly opened Thread, in Call moveToThread(this) in the constructor.
Applicable to transactional operations, such as file read and write, database read and write.

2、Worker-Object

Before Qt4.4, run was a pure virtual function, and QThread must be subclassed to implement the run function.
Starting from Qt4.4, QThread no longer supports abstract classes, run calls QThread::exec() by default, no need to subclass QThread, only need to subclass a QObject.
It has no meaning to realize multi-threading through inheritance. QThread is the interface or control point of the operating system thread, which is used to act as a collection of thread operations.
Use Worker-Object to move them into threads with QObject::moveToThread.
The method of specifying the thread entry function of a thread object:
A. Define a slot function void tmain() in the class as the thread entry function
B. Define a QThread member object m_thread in the class
C. Change the thread dependency of the current object to m_thread
D. Connect the started() signal of m_thread to the tmain slot function.

#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class Worker : public QObject
  QThread m_thread;
protected slots:
  void tmain()
    qDebug() << "void tmain()";
  explicit Worker(QObject* parent = 0):QObject(parent)
    moveToThread(&m_thread);
    connect(&m_thread, SIGNAL(started()), this, SLOT(tmain()));
  void start()
    m_thread.start();
  void terminate()
    m_thread.terminate();
  void exit(int c)
    m_thread.exit(c);
  ~Worker()
    m_thread.wait();
#endif // WORKER_H

9. Communication between multithreading and GUI components

1. The basis of multithreading and GUI component communication

The design principle of the GUI system:
the creation of all interface components can only be done in the GUI thread (main thread). There are two ways to communicate between sub-threads and interface components:
A. Signal slot method
B. Send custom event method

2. Signal slot mode

The scheme of using signal slots to solve the communication between multi-threads and interface components:
A. Define the update signal of the interface component in the sub-thread
B. Define the slot function for updating the interface component in the main window class
C. Connect the update signal to the slot in an asynchronous manner Function
The child thread updates the interface components by sending signals, and all interface component objects can only be attached to the GUI thread (main thread).
The essence of the sub-thread updating the interface state is that the sub-thread sends a signal to notify the main thread of the interface update request, and the main thread modifies the interface components according to the specific signal and signal parameters.
Use the signal slot to update the progress display information of the progress bar in the main interface in the sub-thread.
Worker class:

signals:
  void signalProgressValue(int value);
    work();
    exec();
    moveToThread(this);
  void work()
    for(int i = 0; i < 11; i++)
        emit signalProgressValue(i*10);
        sleep(1);

Main interface class:

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QProgressBar>
#include "WorkThread.h"
class Widget : public QWidget
  QProgressBar* m_progress;//进度条
  WorkThread* m_thread;//工作线程
  Widget(QWidget *parent = 0):QWidget(parent)
    m_progress = new QProgressBar(this);
    m_progress->move(10, 10);
    m_progress->setMinimum(0);
    m_progress->setMaximum(100);
    m_progress->setTextVisible(true);
    m_progress->resize(100, 30);
    m_thread = new WorkThread();
    m_thread->start();
    connect(m_thread, SIGNAL(finished()), m_thread, SLOT(deleteLater()));
    //连接工作线程的信号到界面的槽函数
    connect(m_thread, SIGNAL(signalProgressValue(int)), this, SLOT(onProgress(int)));
  ~Widget()
  void onProgress(int value)
    m_progress->setValue(value);
#endif // WIDGET_H

Main function:

#include "Widget.h"
#include <QApplication>
  QApplication a(argc, argv);
  Widget w;
  w.show();
  return a.exec();

3. How to send custom events

A. Custom events are used to describe the update details of the interface
. B. Rewrite the event processing function event in the main window class.
C. Use the postEvent function (asynchronously) to send custom event class objects.
The child thread specifies the object to receive messages as the main window object. , update the interface state in the event event processing function.
The event object is processed in the main thread, and the event function is called in the main thread.
The sent event object must be created in the heap space.
The child thread must be created with the address information of the target object.
Custom event class:

#ifndef PROGRESSEVENT_H
#define PROGRESSEVENT_H
#include <QEvent>
class ProgressEvent : public QEvent
  int m_progress;
  const static Type TYPE = static_cast<Type>(QEvent::User + 0xFF);
  ProgressEvent(int progress = 0):QEvent(TYPE)
    m_progress = progress;
  int progress()const
    return m_progress;
#endif // PROGRESSEVENT_H

Custom thread class:

#include <ProgressEvent.h>
        QApplication::postEvent(parent(), new ProgressEvent(i*10));

Custom interface class:

#ifndef WIDGETUI_H
#define WIDGETUI_H
#include "ProgressEvent.h"
class WidgetUI : public QWidget
  WidgetUI(QWidget *parent = 0):QWidget(parent)
    m_thread->setParent(this);
  ~WidgetUI()
    m_thread->quit();
  bool event(QEvent *event)
    bool ret = true;
    if(event->type() == ProgressEvent::TYPE)
        ProgressEvent* evt = dynamic_cast<ProgressEvent*>(event);
        if(evt != NULL)
            //设置进度条的进度为事件参数的值
            m_progress->setValue(evt->progress());
    else
        ret = QWidget::event(event);
    return ret;
#endif // WIDGETUI_H
#include "WidgetUI.h"
  WidgetUI w;

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

Guess you like

Origin blog.csdn.net/m0_60259116/article/details/130495422