QT from entry to penetration (V) -- QThread

Posted by SpiderSprog on Sat, 15 Jan 2022 10:25:40 +0100

introduction
The previous articles have made a simple summary of C + + threads, talking about multithreading in C++11 (III) - only their own powerful blog Garden (cnblogs.com). This paper focuses on the summary and implementation of Qt multithreading.

Much like C++11, Qt uses QThread to manage threads. A QThread object manages a thread. There are many similarities in use with C++11, but more are unique contents in Qt. In addition, the QThread object also has a message loop exec() function, that is, each thread has a message loop to handle its own thread events.

1, Knowledge review
First, let's review some knowledge points:

1. Why multithreading?

To solve the problem that time-consuming operations block the whole program, we usually put time-consuming operations into sub threads

Process: an independent program with an independent virtual address space needs to use the process communication mechanism to communicate with other processes.

Threads: without their own resources, they share the virtual address space of processes, and there are hidden dangers in the communication of multiple threads.

ps: in the operating system, each process has independent memory space. The overhead of threads is much less than that of processes. A process can have multiple threads. (therefore, we often use multi-threaded concurrency instead of multi process concurrency)

To better understand the role of multithreading, let's take a look at an example:

Run a 10s time-consuming operation in the main thread. (triggered by button)

#include "threadtest.h"
#include"qthread.h"
Threadtest::Threadtest(QWidget* parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    connect(ui.btn_start, &QPushButton::clicked, this, &Threadtest::on_pushButton_clicked);

}
void Threadtest::on_pushButton_clicked()
{
    QThread::sleep(10);//Main thread
}

It can be seen that during the running of the program, the whole thread is responding to the time-consuming operation of 10 seconds, and the message loop exec() function of the thread does not respond (that is, you drag the interface during this process without response)

2, Thread class QThread
Qt provides a thread class through which you can create sub threads. Qt provides two ways to create sub threads, which will be described in turn later. Let's take a look at some common API functions provided in this class:

2.1 common member functions

// QThread class common API s
// Constructor
QThread::QThread(QObject *parent = Q_NULLPTR);
// Determine whether the task in the thread has been processed
bool QThread::isFinished() const;
// Determine whether the child thread is executing a task
bool QThread::isRunning() const;

// Threads in Qt can set priorities
// Gets the priority of the current thread
Priority QThread::priority() const;
void QThread::setPriority(Priority priority);
priority:
    QThread::IdlePriority        --> Lowest priority
    QThread::LowestPriority
    QThread::LowPriority
    QThread::NormalPriority
    QThread::HighPriority
    QThread::HighestPriority
    QThread::TimeCriticalPriority
    QThread::InheritPriority    --> Highest priority, The default is this


// Exit the thread and stop the underlying event loop
// Exit the working function of the thread
void QThread::exit(int returnCode = 0);
// After calling the thread exit function, the thread will not exit immediately, because the current task may not be completed. Calling back this function is
// Wait for the task to complete, and then exit the thread. Generally, this function will be called after exit()
bool QThread::wait(unsigned long time = ULONG_MAX);

2.2 signal slot

// The effect is the same as calling exit()
// After substituting this function, call the wait() function
[slot] void QThread::quit();
// Start child thread
[slot] void QThread::start(Priority priority = InheritPriority);
// When a thread exits, it may terminate the thread immediately. Generally, this function is not used
[slot] void QThread::terminate();

// This signal is issued when the task executed in the thread is completed
// The processing logic in the task function has been executed
[signal] void QThread::finished();
// This signal is sent before starting work and is generally not used
[signal] void QThread::started();

2.3 static functions

// Returns a pointer to the QThread that manages the current execution thread
[static] QThread *QThread::currentThread();
// Returns the ideal number of threads that can run on the system = = the same number of CPU cores as the current computer
[static] int QThread::idealThreadCount();
// Thread sleep function
[static] void QThread::msleep(unsigned long msecs);    // Unit: ms
[static] void QThread::sleep(unsigned long secs);    // Unit: Second
[static] void QThread::usleep(unsigned long usecs);    // Unit: microsecond

3, Two methods of multithreading in Qt
🧡🧡 3.1. Method for deriving QThread class object (overriding Run function)

First, explain the steps in text.

Customize your own class to inherit from QThread class;
Override the virtual function run() in the QThread class in the custom class.
This is probably the use of polymorphism in C + +. One more point: QThread class inherits from QObject class.

Let's focus on the run() function. As the entry of the thread, that is, the thread starts to execute from run(). All the work we intend to complete in the thread should be written in the run() function. Personally, I think the run() function can be understood as a thread function. This means that the subclass overrides the virtual function of the base class. The run() function of the QThread of the base class simply starts the exec() message loop. There are many things to say about this exec(). Please be prepared.
Then let's try to implement 10s time-consuming operation with multithreading: (trigger with button)

one ️⃣ After editing the ui interface, first create a class of workThread. (inherited from QThread class)

two ️⃣ Override the run function in the class of workThread1
In the workthread Declared run function of H:

#include <qthread.h>
class workThread : public QThread
{
public:
    void run();
};

In the workthread Rewrite the run function in CPP (and print the ID of the child thread):

#include "workThread.h"
#include"qdebug.h"
workThread::workThread(QObject* parent)
{

}
//Override run function
void workThread::run()
{
    qDebug() << "Current child thread ID:" << QThread::currentThreadId();
    qDebug() << "Start thread execution";
    QThread::sleep(10);
    qDebug() << "Thread end";
}

three ️⃣ Start the thread in the main class
threadtest.h declares thread and button events

#include <QtWidgets/QMainWindow>
#include "ui_threadtest.h"
#include"workThread.h"
#pragma execution_character_set("utf-8")
class Threadtest : public QMainWindow
{
    Q_OBJECT

public:
    Threadtest(QWidget *parent = Q_NULLPTR);

    
private:
    Ui::ThreadtestClass ui;
    void btn_clicked();
workThread* thread;
};

threadtest.cpp, and start the child thread

#include "threadtest.h"
#include"qthread.h"
#include"qdebug.h"

Threadtest::Threadtest(QWidget* parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    connect(ui.btn_start, &QPushButton::clicked, this, &Threadtest::btn_clicked);
   thread = new workThread(this);  
}
void Threadtest::btn_clicked()
{
    qDebug() << "Main thread id: " << QThread::currentThreadId();
    thread->start();//Start child thread
}

It can be realized. You can also drag the interface when performing time-consuming operations.

It should be noted that:

When adding the header file of workThread class in the main program, you need to add workThread H in threadtest H (otherwise, an error will be reported

Use QThread::currentThreadId() to view the ID of the current thread. Whether it is a child thread or a main thread, the ID of different threads is different. Note that this is a static function, so it can be called without an object.

The execution of the created workThread1 class is actually in the main thread. Only the program in the run function will be executed in the sub thread! (that is, QThread is only the thread management class, and only run() is our thread function)

Therefore, the member variables in QThread (i.e. the created class) belong to the main thread. Before accessing, you need to judge whether the access is safe. The variable created in run () belongs to the child thread.

Sharing memory between threads is unsafe (because multi threads competing for resources will affect data security). The solution is to lock it.

About mutex

Mutex is a simple method of locking to control access to shared resources. As long as a thread is locked, it will forcibly occupy the access to public resources, and other threads cannot access it until the thread is unlocked, so as to protect shared resources.

There are two common methods for mutex locking in Qt:

lock and unlock under QMutex class

//You need to reference #include < qmutex > in the header file
//And declare QMutex mutex in the private of the header file;

mutex.lock()
public_value++;//Public member variable
mutex.unlock();

lock under QMutexLocker class (after locking, it will be unlocked automatically when the destructor is executed)

//You need to reference #include < qmutexlocker > and include < qmutex > in the header file
//And declare QMutex mutex in the private of the header file;

QMutexLocker lock(&mutex);//Execute mutex. When executing the constructor lock()
public_value++;

//Mutex. Is executed when the destructor is executed unlock()

About exec() message loop

Personally, exec() is too important and not easy to understand.

For example, there are two execs () in the following code. We say "one mountain can't have two tigers". Here, that is, two execs () can't be run simultaneously in a thread, otherwise another message loop won't get messages. For example, the exec() in the QDialog modal window cannot receive the user's message because two execs () are opened in the main thread at the same time. But! But! But! We don't have any problems here, because they don't appear in the same thread. One is exec() in the main thread and the other is exec() in the child thread.

#include <QApplication>
#include <QThread>
#include <QDebug>
 
class MyThread:public QThread
{
    public:
        void run()
        {
            qDebug()<<"child thread begin"<<endl;
            qDebug()<<"child thread"<<QThread::currentThreadId()<<endl;
            QThread::sleep(5);
            qDebugu()<<"QThread end"<<endl;
            this->exec();
        }
};
 
int main(int argc,char ** argv) //mian() as the main thread
{
    QApplication app(argc,argv);
 
    MyThread thread; //To create a QThread derived class object is to create a child thread
    thread.start(); //Start the child thread, and then the thread function run() will be called automatically
 
    qDebug()<<"main thread"<<QThread::currentThreadId()<<endl;
    QThread::sleep(5);
    qDebugu()<<"main thread"<<QThread::currentThreadId()<<endl;
 
    thread.quit(); //Using quit() or exit() enables the child thread to exit the message loop without getting stuck in the child thread
    thread.wait(); //Wait for the child thread to exit, and then recycle the resources
                   //thread.wait(5000); // Set waiting time
    
    return app.exec();    
}

If the exec() message loop function is not executed in the run() function, the execution of run() means that the child thread exits. Generally, when the child thread exits, the main thread needs to recover resources. You can call QThread wait() to make the main thread wait for the child thread to exit, and then recover resources. Here, wait () is a blocking function, a bit like the join() function in C++11.

But! But! But! The exec() function is called in the run() function, exec() is a message loop, also known as the event loop, and it is also blocked. It is equivalent to a dead loop which makes the child thread here never quit. We must call QThread's quit() function or exit() function to make the child thread quit the message loop, and sometimes it doesn't quit immediately. You need to wait until the control of the CPU is handed over to the thread's exec().

So thread quit(); Loop the message to exit the child thread, and then thread wait(); Reclaim the resources of the child thread in the main thread.

There are two points to note: the exet() message loop of the child thread must be invoked in the run() function. If there is no message loop, there is no need to call quit() or exit(), because the call will not work.

The first way to create a thread needs to explicitly call exec() in run(), but what is the role of exec(), which can not be seen at present, and can only be known in the second way to create a thread.

💜💜 3.2. Use signal and slot mode to realize multithreading

I've just finished using the method of QThread derived class object to create a thread. Now I'll speak ill of it. This method has a limitation. Only one run() function can run in a thread, but when multiple functions run in the same thread, there is no way, at least it is troublesome to implement. Therefore, Dangdang, the second way to create a thread is to use signals and slots, that is, to define a function executed in a thread (we can call it a thread function) as a slot function.

It is still the first step to explain the steps of this method in written form.

Create a new class (mywork), let this class derive from QObject, and add a public member function (working) in this class. The function body is the business logic to be executed in the sub thread

class MyWork:public QObject
{
public:
    .......
    // The function name can be specified by yourself, and the parameters can be added according to the actual needs
    void working();
}

Create a QThread object in the main thread, which is the object of the child thread

QThread* sub = new QThread;

Create a working class object in the main thread (never assign a parent object to the created object)

MyWork* work = new MyWork(this);    // error
MyWork* work = new MyWork;          // ok

To move the MyWork object to the created child thread object, you need to call the moveToThread() method provided by the QObject class

// void QObject::moveToThread(QThread *targetThread);
// If a parent object is specified for work, the function call fails
// Prompt: QObject::moveToThread: Cannot move objects with a parent
work->moveToThread(sub);    // Move to work in child threads

Start the child thread and call start(). At this time, the thread starts, but the object moved to the thread does not work
Call the working function of the MyWork class object to start the function execution. At this time, it is running in the sub thread moved to
Code example:

one ️⃣ Create a class of workThread. (inherited from QThread class)
Define slot function (programs executed by sub threads can be placed in slot function)

//workThread.cpp (first declare the slot function in workThread.h)

void workThread:: doWork()
  {
      qDebug()<<"Current thread ID:"<<QThread::currentThreadId();
      qDebug()<<"Start execution";
      QThread::sleep(10);    
      qDebug()<<"End execution";
  }

two ️⃣ Instantiate the workThread class and QTread class in the main thread
In threadtest Declaration in H

#include <QtWidgets/QMainWindow>
#include "ui_threadtest.h"
#include"workThread.h"
#include"qthread.h"
#pragma execution_character_set("utf-8")
class Threadtest : public QMainWindow
{
    Q_OBJECT

public:
    Threadtest(QWidget *parent = Q_NULLPTR);  
private:
    Ui::ThreadtestClass ui;
    void btn_clicked();
    workThread* thread; //Instantiate the workThread class
    QThread* qthread;   //Instantiate QThread class
};

In threadtest CPP, put yourself into the thread QThread object through moveToThread, and finally start the thread

#include "threadtest.h"
#include"qthread.h"
#include"qdebug.h"

Threadtest::Threadtest(QWidget* parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
   
    //You cannot specify that the parent class of an object of a custom class is a widget, that is, there is no this (very important!!!)
   thread = new workThread();  
   qthread1 = new QThread(this);
   thread->moveToThread(qthread1);
   //Clean up thread memory at end of thread
   connect(qthread1, &QThread::finished, qthread1, &QThread::deleteLater);
   //Bind button events (signals) to slot functions
   connect(ui.btn_start, &QPushButton::clicked, thread, &workThread::dowork);
   //Print main thread
   connect(ui.btn_start, &QPushButton::clicked, [=]() {
       qDebug() << "Main thread id: " << QThread::currentThreadId();
       });
   //Thread start
   qthread1->start();
}

When running the slot function, it cannot be called directly here (e.g. thread1 - > dowork()). Instead, connect the signal and slot

Here, the print main thread and slot function are bound to the click event of the button button.

Special attention should be paid to (pit climbing record):

UI cannot be operated in child thread
The child thread created by Qt cannot perform any operation on UI objects, that is, QWidget and its derived class objects. This is the first pit I fell. It may be that the Qt sub thread cannot perform any processing on the interface, including the pop-up of message boxes, due to security issues. The correct operation should be to pass some parameters to the main thread through the signal slot for the main thread (that is, the Controller) to process.
A custom class cannot specify a parent object
For example, in the above program: (you can't specify a custom class object as a widget, that is, you can't add this)

thread1=new workThread1();//initialization

Relationship between QThread and connect
What we use most is the relationship between QThread and connect. When using the connect function, we generally ignore the last parameter. At this time, we need to look at the function prototype:

[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)

As you can see, the last parameter represents the connection method. We generally use three methods:

Autoconnection: the default connection method. If the signal and slot, that is, the sender and receiver are in the same thread, it is equivalent to direct connection; If the sender and receiver are in different threads, it is equivalent to a queue connection.

Direct connection: when the signal is transmitted, the slot function will be called directly. No matter which thread the slot function belongs to, the slot function is executed in the thread of the emitter.

Queued connection: when control returns to the event loop of the thread where the receiver is located, the slot function is called. The slot function is executed on the receiver's thread.

Therefore, we should pay attention to:

The main thread object sends a signal to connect the slot function of QThread subclass. The QThread subclass object is created in the main thread. No matter which connection method is adopted, the slot function belongs to the main thread. (if the slot function is called in the rewritten run function, then the slot function is executed on the secondary thread, and data security is noticed).
The secondary thread sends a signal in the run, and the slot function can be the slot function of the signal sending object itself. It collects itself spontaneously, which are all rows in the secondary thread. Slot function is the slot function of QThread subclass or the slot function of the object in the main thread. In this case, you need to specify the connection mode in connect in run. If you connect directly, the slot function will be executed in the secondary thread (data security problems may occur), and the queue will be executed in the main thread.
🧡🧡 To sum up:

Be sure to use the signal slot mechanism. Don't think about calling directly. You will find that it is not executed in the sub thread.

A custom class cannot specify a parent object because the moveToThread function will specify the thread object as the parent object of the custom class. An error will be reported when the custom class object already has a parent object.

When a variable needs to be accessed between multiple threads, it is best to add the volatile keyword to avoid reading the old value. Of course, Qt provides thread synchronization support, such as mutex locks. It is safer to use these methods to access variables.

Analyze the signaling object and the thread where the signaling object is located, and then judge where the slot function is executed through the connection mode. (Xiaobai creates objects in run when in use - because many non slot functions need to be executed in the secondary thread, they are led out through the pointer, and then connected to interact with other modules, indicating that the connection mode is in the form of queue, so the relevant execution is executed in the secondary thread). Remember here that moveToThread can only move slot functions to secondary threads.

Operation idea of starting multithreading

If we need to implement a sorting operation, that is, first obtain 1000 random numbers, and then sort them by bubble sorting method.

Method 1: rewrite the run function

Idea: (build two sub threads, one for generating random numbers and one for bubble sorting, and the main thread is responsible for calling)

Create a new myThread class and override the run function (that is, generate random numbers) in this class. The number of random numbers to be generated (scvnum signal) sent by the main thread shall be received before generation. After generation, a sendArray signal shall be sent
Create a new BubbleSort class and override the run function (i.e. bubble sorting algorithm) in this class. It is necessary to receive the generated random number (rcvArray signal) before sorting, and send a finish signal after sorting
The main thread tells the myThread thread the number of random numbers to be generated through the static signal, and then the myThread thread receives the scvnum signal to generate random numbers, and then sends a sendArray signal (i.e. generate a random number set)
The BubbleSort sub thread receives the sendArray signal (i.e. random number) through the rcvArray signal, then performs bubble sorting, and then sends the finish signal
After receiving the finish signal, the main thread displays the sorted random number on the interface
Implementation code:

Child threads h

#pragma once
#include"qthread.h"
#include"qvector.h"
//New random number class
class myThread :
    public QThread
{
    Q_OBJECT
public:
    myThread(QObject* parent = nullptr);
    void scvnum(int num);//Receive digital
protected:
    void run();
signals:
    void sendArray(QVector<int>num);//send out
private:
    int m_num;
};

//New bubble sort class
class BubbleSort :
    public QThread
{
    Q_OBJECT
public:
    BubbleSort(QObject* parent = nullptr);
    void rcvArray(QVector<int>list);//The number to receive is sorted
protected:
    void run();
signals:
    void finish(QVector<int>list);//Send a finish signal after sorting
private:
    QVector<int>m_list;
};

Child threads cpp

#include "myThread.h"
#include"qelapsedtimer.h"
#include"qdebug.h"

myThread::myThread(QObject* parent) :QThread(parent)
{

}
BubbleSort::BubbleSort(QObject* parent) : QThread(parent)
{

}


void myThread::scvnum(int num)
{
    m_num = num;
}

void myThread::run()
{
    qDebug() << "child thread id" << QThread::currentThreadId();
    QVector<int> list;
    QElapsedTimer time;
    time.start();
    for (int i = 0; i < m_num; i++)
    {
        list.push_back(qrand() % 100000);
    }
    int milsec = time.elapsed();
    qDebug() << "The number of" << m_num << "generated";
    qDebug() << "shared" << milsec << "second";
    emit sendArray(list);
}

void BubbleSort::rcvArray(QVector<int> list)
{
    m_list = list;
}

void BubbleSort::run()
{
    qDebug() << "BubbleSort thread id:" << QThread::currentThreadId();
    QElapsedTimer time;
    time.start();
    int temp;
    for (int i = 0; i < m_list.size(); ++i)
    {
        for (int j = 0; j < m_list.size()-i-1; ++j)
        {
            if (m_list[j] > m_list[j + 1])
            {
                temp = m_list[j];
                m_list[j] = m_list[j + 1];
                m_list[j + 1] = temp;
            }
        }
    }
    int milsec = time.elapsed();
    qDebug() << "shared" << milsec << "second";
    emit finish(m_list);
}

Main thread h

#include <QtWidgets/QWidget>
#include"ui_list.h"

class list : public QWidget
{
    Q_OBJECT

public:
    list(QWidget *parent = Q_NULLPTR);
signals:
    void stating(int num);//Set the number of random numbers to generate

private:
    Ui::listClass ui;
};

Main thread cpp

#include "list.h"
#include"myThread.h"
list::list(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    //1. Create a child thread object
    myThread* thread = new myThread();
    BubbleSort* bub_thread = new BubbleSort();
    //Send the random number to be generated to the child thread
    connect(this,&list::stating, thread, &myThread::scvnum);
    //2 start the child thread
    connect(ui.pushButton, &QPushButton::clicked,  [=]() {
        emit stating(1000);//The main thread sets the number of random number of sub threads
        thread->start();
    
        
        });
    connect(thread, &myThread::sendArray, bub_thread, &BubbleSort::rcvArray);

    //3 receive the data sent by the sub thread
    connect(thread, &myThread::sendArray,  [=](QVector<int>list) {
        for (int i = 0; i < list.size(); ++i)
        {
            ui.randlist->addItem(QString::number(list.at(i)));
        }
        });
    connect(thread, &myThread::sendArray, [=](QVector<int>list){
        bub_thread->start();
        });
    connect(bub_thread, &BubbleSort::finish, [=](QVector<int>list) {
        for (int i = 0; i < list. size(); ++i)
        {
            ui.bubblelist->addItem(QString::number(list.at(i)));
        }
        });
}

Effect achieved:

Method 2: moveToThread()

Idea:

Create a new myThread class to generate random numbers (working function). After receiving the signal from the main thread, start generating random numbers
Create a new BubbleSort class for sorting (working function). Start sorting after receiving the random number generated by myThread class
Finally, it is displayed in the interface
Code implementation:

Child threads h

#pragma once
#include"qthread.h"
#include"qvector.h"
#include"qobject.h"
//New random number class
class myThread :
    public QObject
{
    Q_OBJECT
public:
    myThread(QObject* parent = nullptr);
    void working(int num);//Generate random number
signals:
    void sendArray(QVector<int>num);//send out
private:
    int m_num;
};

//New bubble sort class
class BubbleSort :
    public QObject
{
    Q_OBJECT
public:
    BubbleSort(QObject* parent = nullptr);
    void working(QVector<int>list);//The number to receive is sorted

signals:
    void finish(QVector<int>list);//Send a finish signal after sorting

};

Child threads cpp

#include "myThread.h"
#include"qelapsedtimer.h"
#include"qdebug.h"

myThread::myThread(QObject* parent) :QObject(parent)
{

}
BubbleSort::BubbleSort(QObject* parent) : QObject(parent)
{

}


void myThread::working(int num)
{
    qDebug() << "child thread id" << QThread::currentThreadId();
    QVector<int> list;
    QElapsedTimer time;
    time.start();
    for (int i = 0; i < num; i++)
    {
        list.push_back(qrand() % 100000);
    }
    int milsec = time.elapsed();
    qDebug() << "The number of" << m_num << "generated";
    qDebug() << "shared" << milsec << "second";
    emit sendArray(list);
}


void BubbleSort::working(QVector<int>list)
{
    qDebug() << "BubbleSort thread id:" << QThread::currentThreadId();
    QElapsedTimer time;
    time.start();
    int temp;
    for (int i = 0; i < list.size(); ++i)
    {
        for (int j = 0; j < list.size()-i-1; ++j)
        {
            if (list[j] > list[j + 1])
            {
                temp = list[j];
                list[j] = list[j + 1];
                list[j + 1] = temp;
            }
        }
    }
    int milsec = time.elapsed();
    qDebug() << "shared" << milsec << "second";
    emit finish(list);
}

Main thread h

#include <QtWidgets/QWidget>
#include"ui_list.h"

class list : public QWidget
{
    Q_OBJECT

public:
    list(QWidget *parent = Q_NULLPTR);
signals:
    void stating(int num);//Set the number of random numbers to generate

private:
    Ui::listClass ui;
};

Main thread cpp

#include "list.h"
#include"myThread.h"
list::list(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    //1. Create QThread object
    QThread* thread1 = new QThread;
    QThread* thread2 = new QThread;
    //2. Create sub thread class objects
    myThread* myth = new myThread();
    BubbleSort* bub = new BubbleSort();
    //3. Move the child thread object into the QThread object through movetothread
    myth->moveToThread(thread1);
    bub->moveToThread(thread2);
    //4 start sub thread

    //Send the random number to be generated to the child thread first
    connect(this,&list::stating, myth, &myThread::working);
    //Restart the child thread
    connect(ui.pushButton, &QPushButton::clicked,  [=]() {
        emit stating(1000);//The main thread sets the number of random number of sub threads
        thread1->start();        
        });
    //Send the generated random number to the BubbleSort class
    connect(myth, &myThread::sendArray, bub, &BubbleSort::working);

    //Display the generated random number on the interface
    connect(myth, &myThread::sendArray,  [=](QVector<int>list) {
        for (int i = 0; i < list.size(); ++i)
        {
            ui.randlist->addItem(QString::number(list.at(i)));
        }
        });
    //Start sorting algorithm while sending
    connect(myth, &myThread::sendArray, [=](QVector<int>list){
        thread2->start();
        });
    //Display the sorted numbers in the interface
    connect(bub, &BubbleSort::finish, [=](QVector<int>list) {
        for (int i = 0; i < list. size(); ++i)
        {
            ui.bubblelist->addItem(QString::number(list.at(i)));
        }
        });
}

conclusion

Through comparison, we can find that:

Because of the second method, we can customize the sub thread running function with parameters, so the code is more concise.
In the second method, we can also modify which thread we need to run in at will, and the code is more flexible.
The first method is suitable for processing a single event in a thread. Its logic is simple (you only need to create an object inherited from QThread class, rewrite the run function, and then start). For processing multiple events in a thread, it is better to use the second method.
Why can't you add a parent class to the defined child thread object in the second method Since the parent class is added, it can no longer be moved to QThread
How to release thread resources?

When creating a new object, directly use this to specify its parent class (that is, put it into the number of objects)
Free resources at the end of the program

connect(this, &list::destroyed, this, [=]() {
    thread1->quit();
    thread1->wait();
    thread1->deleteLater();
    thread2->quit();
    thread2->wait();
    thread2->deleteLater();
    myth->deleteLater();
    bub->deleteLater();
    });

Topics: C++ OpenCV Qt UI