Really understand the multi-threading of Qt5: create a multi-threaded console program (using movetothread method) and attach the download address of Baidu Netdisk

There are many online articles about Qt multi-threading application examples, few of them really explain the principle of Qt multi-threading. Later, I found out that there were two articles written by an author named "Wan Zhang Gao Building on the Ground" on Zhihu:
"Qt Multi-threaded Programming : Knock the Door of QThread Class" https://zhuanlan.zhihu.com/p/53270619
"Qt Middle Multi-threading technology" https://zhuanlan.zhihu.com/p/52612180
plus the official documentation and routines, basically figured it out, modified on the basis of the official routines, but also for you to learn from Qt者 Reference. This article also refers to part of the content of the above two articles. Implement multithreading for the second method (moveToThread) recommended by the government.

Key concept

Let me talk about a few key points first. For detailed theory, please refer to the above two articles:

(1) The QThread class inherits from QObject. OneQThread instanceA thread in the hypervisor. The execution of QThread starts with run(). The run function calls exec() by default to implement the event loop. Putting your own code implementation into the run function will make the run function bloated, difficult to maintain, and not conducive to object-oriented development. When rewriting the run function, if you forget to call exec() in the run() function, there will be no event loop, and the thread will end after the run is executed.

(2) The moveToThread() function in QObject can still run in a new thread without destroying the class structure. You can use QObject::moveToThread() to move a custom class object (Worker class) to a thread container (an instance of QThread class) for use. The access and control of the Worker class are realized through the methods in the thread container and the signal-slot mechanism of Qt.

Insert picture description here

(3) In which thread are the controller and worker objects? The phrase "where you create it belongs where you belong" applies everywhere. The moveToThread() function is to changeSlot functionIs called in the specified thread. In other words, the controller and worker objects are inMain threadin. Except for those bound to the controllerSlot function (including functions called in the body of the slot function)In addition, the rest of the worker functions are alsoMain threadIn execution.

Purpose

In this experiment, a simple command interactive terminal (while loop) was implemented in the main function. The user can send commands to control the start, stop and check of each thread. Create two thread containers (controller1 and controller2), the specific things that need to be done in each thread are implemented by the on_doSomething() function. (In this experiment, the thing to do is to keep the variable count accumulating by 1).

experiment procedure

(1) Create a subclass Worker based on QObject. This class has a member function on_doSomething(). If the code running in this function is very time-consuming. Therefore, you need to "move" this class object to a new thread. Here put on_doSomething into the while() in the Workerloop function. Start the loop body by executing the startWorker function. In this way, the ""time-consuming work"" runs in a new thread. The loop body in Workerloop will determine whether to stop according to the value of the member variable isStop.
The Worker class needs a slot function to execute external commands, and a signal to send results to the outside world. There is a signal signal in the main thread to associate and trigger the startWorker function (the signal-slot mechanism is not used to change the value of isStop, but the closeWorker function is called directly in the main thread). At the same time, there is a signal signal (resultReady) in the Worker class to send the result of the operation to the outside world (in this experiment, there is a command interaction while() in the main function, so it cannot be displayed in real time during the operation, and can only be displayed at the end of the command interaction After display, so it is not used). Such as the following code:

#ifndef WORKER_H
#define WORKER_H
#include <QObject>

class Worker : public QObject
{
    
    
    Q_OBJECT
public:
    explicit Worker( QString name,QObject *parent = nullptr);
signals:
    void resultReady(const QString &str);//向外界发送结果,本实验中没有使用
public slots:
    void startWorker();
    void closeWorker();
private:
    void Workerloop();//事件循环
    void on_doSomething();//耗时操作
private:
    bool isStop;
    int count;
    QString m_name;
};
#endif // WORKER_H

In order to reflect the execution in different threads, we print the current thread ID in each function.

#include "worker.h"
#include <QDebug>
#include <QThread>

Worker::Worker(QString name,QObject *parent) : QObject(parent)
{
    
    
    m_name =  name;
    qDebug()<<"I'm "<< m_name <<" initialized in thread: "<<QThread::currentThreadId();
    isStop=false;
    count = 0;
}
void Worker::startWorker()
{
    
    
    qDebug()<<"I'm "<< m_name <<" startWorker in thread: "<<QThread::currentThreadId();
    isStop = false;
    Workerloop();
}
void Worker::closeWorker()
{
    
    
     qDebug()<<"I'm "<< m_name <<" closeWorker in thread: "<<QThread::currentThreadId();
    isStop = true;
}
void Worker::Workerloop()
{
    
    
     qDebug()<<"I'm "<< m_name <<" Workerloop in thread: "<<QThread::currentThreadId();
    while(!isStop)
    {
    
    
        on_doSomething();
        QThread::sleep(1);
    }
}
void Worker::on_doSomething()
{
    
    
    count++;
    qDebug()<<"I'm "<< m_name <<" working in thread: "<<QThread::currentThreadId();
 /*   QString workmsg;
    workmsg.sprintf("conut=: %d",count);
    emit resultReady(workmsg);*/
}

(2) Create a QObject-based Controller class to control the start and stop of threads. A similar signal to start the Worker class can be designed in the Controller class as the "outside", but the QThread::started() signal is used instead. The on_receivResult() slot function is used to receive the running result of the new thread (not used). The Controller slot corresponds to the Worker signal.

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>
#include <QThread>
#include "worker.h"

class Controller : public QObject
{
    
    
    Q_OBJECT
public:
    explicit Controller(int ctrlId,QString workname,QObject *parent = nullptr);
    ~Controller();
    void start();
    void stop();
signals:    

public slots:
    void on_receiveResult(const QString &str);
public:
bool isRunning;
private:
    QThread m_workThread;//线程容器。直接实例化,需要在析构函数中quit()+wait()
    Worker *m_worker;//线程中的对象 new在堆上,需用deleteLater().
    int m_ctrlId;
    QString m_workname;
};
#endif // CONTROLLER_H

The instance of the QThread class can start the function to run in a new thread by calling the started signal issued by the start() function.
The essence of a normal exit thread is to exit the event loop, that is, execute the exit(int returnCode = 0) function. Return 0 means success, other non-zero values ​​mean abnormal. The quit() function is equivalent to exit(0). The finished() signal will be issued after the thread exits.

#include "controller.h"
#include <QDebug>
Controller::Controller(int ctrlId,QString workname,QObject *parent) : QObject(parent)
{
    
    
    m_ctrlId = ctrlId;
    m_workname = workname;
    qDebug()<<"Controller is running in main thread: "<<QThread::currentThreadId();
    isRunning = false;
    m_worker = new Worker(workname);
    m_worker->moveToThread(&m_workThread);

    connect(&m_workThread,&QThread::started,m_worker,&Worker::startWorker);
    connect(&m_workThread,&QThread::finished,m_worker,&QObject::deleteLater);
    connect(m_worker,&Worker::resultReady,this,&Controller::on_receiveResult);
}
Controller::~Controller()
{
    
    
	if(m_workThread.isRunning())
    {
    
    
    	m_worker->closeWorker();
        m_workThread.quit();
        m_workThread.wait();
     }
}
void Controller::start()
{
    
      
    m_workThread.start();
    isRunning = true;
}
void Controller::stop()
{
    
    
    if(m_workThread.isRunning())
    {
    
    
        m_worker->closeWorker();
        m_workThread.quit();
        m_workThread.wait();
        isRunning = false;
    }
}
void Controller::on_receiveResult(const QString &str)
{
    
    
    qDebug()<< str;
}

When the Controller::~Controller() destructor is executed, the m_workThread object will be destroyed. But deleting the running QThread (that is, isFinished() returns false) will cause the program to crash. Therefore, increase m_workThread.quit() and m_workThread.wait();
(3) In the main function, create two instances of the Controller class, with the Id numbers being Thrd0 and Thrd1; the initial names of the Worker class in the Controller class are: "Tom" and "Jerry".
In the main function, use while to start a terminal interaction, and use QTextStream to monitor the command words entered by the keyboard:
exit Exit terminal interaction
start1 Start thread 1
start2 Start thread 2
stop1 Exit thread 1
stop2 Exit thread 2
check1 Check whether thread 1 is running
check2 Check thread 2 Whether to run

#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <controller.h>

enum CTRID{
    
    
    Thrd0,
    Thrd1
};
int main(int argc, char *argv[])
{
    
    
    QCoreApplication a(argc, argv);
    qDebug()<<"The main threadID is: "<< QThread::currentThreadId();

    QString workername1("Tom");
    QString workername2("Jerry");
    Controller controller1(Thrd0,workername1);
    Controller controller2(Thrd1,workername2);

    qDebug()<<"you can enter the following commands by tapping the keyboard:\n";
    qDebug()<<"\tstart1\n\tstart2\n\tstop1\n\tstop2\n\tcheck1\n\tcheck2\n\teixt\n";

    QTextStream qin(stdin);
    QString cmd;
    while(1)
    {
    
    
        qin>>cmd;
        qDebug()<<"I'm running in main: "<<QThread::currentThreadId();
        qDebug()<<"receive:"<<cmd<<endl;

        if(cmd=="exit")
         {
    
    
            controller1.stop();
            controller2.stop();
            qDebug()<<"main had exited.you can close this console. "<<endl;
            break;
        }
        if(cmd=="check1")
            qDebug()<<"controller1 status is: "<<controller1.isRunning<<endl;
        if(cmd=="check2")
            qDebug()<<"controller2 status is: "<<controller2.isRunning<<endl;
        if(cmd=="start1")
        {
    
    
            controller1.start();
            qDebug()<<"controller1 is started"<<endl;
        }
        if(cmd=="start2")
        {
    
    
            controller2.start();
            qDebug()<<"controller2 is started"<<endl;
        }
        if(controller1.isRunning==true)
        {
    
    
            if(cmd=="stop1")
            {
    
    
                controller1.stop();
                qDebug()<<"controller1 is stoped"<<endl;
            }
        }
        if(controller2.isRunning==true)
        {
    
    
            if(cmd=="stop2")
            {
    
    
                controller2.stop();
                qDebug()<<"controller2 is stoped"<<endl;
            }
        }
    }
    return a.exec();
}

Look at the execution effect: After
Insert picture description here
startup, the Controller instantiation and Worker instantiation are completed in the main function. Therefore, Controller1, Controller2, Worker1, and Worker2 all exist in the main thread 0x2524.
Insert picture description here
Enter the "start1" and "start2" commands respectively, and it can be seen that the Workers in the two threads work in two threads, 0x20e0 and 0x1cac.
Input the "stop1" and "stop2" commands respectively to stop two threads.
Note: After stopping the thread, enter start1 and start2 again. Although the thread can be started, the controller cannot execute the constructor and the worker cannot be started, so it cannot output information.

The source code has been uploaded and can be downloaded for free.
https://download.csdn.net/download/SmartTiger_CSL/12158072
Originally, this source code resource was for everyone to download for free, but the CSDN platform was forced to change to require points to download. Therefore, here is a free download address for Baidu Netdisk
Link 1: https://pan.baidu.com/s/1TWvURe7syAVBSaQd8EpTMg
Extraction code: 6d7z
Insert picture description here
Link 2: https://pan.baidu.com/s/1tq5oUv1e09VUCxEg1C4xMQ
Extraction code: qm4g

Guess you like

Origin blog.csdn.net/SmartTiger_CSL/article/details/104324333