Qt main interface stuck solution - some specific implementation methods

Transferred from: (7 messages) solution to the jamming of Qt main interface - some specific implementation methods_ qq_37518975 blog - CSDN blog

 

 

Qt main interface stuck solution - some specific implementation methods


brief introduction
When we write UI files, in many cases, we need the interface to handle some time-consuming operations in the business. At this time, if we do not handle the logic related to the interface, the main interface will get stuck. At this time, we need to use multithreading

 

Logic 1
First, a very simple chestnut

For example, there is such a time-consuming operation in our code

   // The first time-consuming operation
    auto fWhile1 = [] ()
    {
        for (int i = 0; i < 1000000; i++)
        {
            qDebug()<<i<<endl;
        }
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Bind this code to a button event

connect(ui->pushButton1, &QPushButton::clicked, fWhile1);
  • 1

Then click. It's normal to find that the interface is stuck. You can't continue to operate the interface until this code is completed. This code is too unfriendly and halal, so we need to change it.

Logic 2

How to change, you can see this function

QCoreApplication::processEvents
  • 1

Let's take a look at the official website

Processes all pending events for the calling thread according to the specified flags until there are no more events to process.
You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).
In the event that you are running a local loop which calls this function continuously, without an event loop, the DeferredDelete events will not be processed. This can affect the behaviour of widgets, e.g. QToolTip, that rely on DeferredDelete events to function properly. An alternative would be to call sendPostedEvents() from within that local loop.
Calling this function processes events only for the calling thread.
Note: This function is thread-safe.
  • 1
  • 2
  • 3
  • 4
  • 5
  • You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).
  • You can occasionally call this function when the program is busy performing long operations, such as copying files.
    Let's just do this for the time being.
    Then you can make the code like this.
 auto fWhile2 = [] ()
    {
        for (int i = 0; i < 1000000; i++)
        {
            qDebug()<<i<<endl;
            QApplication::processEvents();
        }
    };
    connect(ui->pushButton2, &QPushButton::clicked, fWhile2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

This kind of code actually has some small problems on poorly configured machines, such as my little broken book. There will still be some cards. I think users can generally accept this situation.

Logic 3
In fact, there is another problem with this logic, that is, what if my business code is not a loop? At this time, we can use a new class interface

QtConcurrent::run
  • 1

This class. This class can put a function into a new thread to execute. Plus

QFuture<T>
  • 1

This class can control the start, control and end of this new thread function.
You can check the official documents for details. I'll give you a simple chestnut here

//Time consuming operation
static bool function_needmoretime()
{
    for (int i = 0; i < 1000000; i++)
    {
        qDebug()<<i<<endl;
    }
    return true;
}

    // three
    auto fWhile3 = [] () -> void
    {
        QFuture<bool> future = QtConcurrent::run(function_needmoretime);
        while(!future.isFinished())
        {
            QApplication::processEvents(QEventLoop::AllEvents, 100);
        }
    };
    connect(ui->pushButton3, &QPushButton::clicked, fWhile3);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

The QFuture + QtConcurrent framework is very powerful. It can abstract the synchronous and asynchronous state of threads, so that programmers don't care too much. It's just the simplest chestnut. My little broken book doesn't get stuck at all. The interface is still smooth.

Logical 4-thread
I won't talk more about thread based nonsense. Everyone knows the truth. I go directly to wiki.

Thread (English): thread)It is the smallest unit that the operating system can schedule operations. It is included in the process and is the actual operation unit in the process. A thread refers to a single sequential control flow in the process. Multiple threads can be concurrent in a process, and each thread executes different tasks in parallel. In Unix System V and SunOS Also known as lightweight process( lightweight processes),But lightweight processes refer more to kernel threads( kernel thread),And put the user thread( user thread)Called threads.
Thread is the basic unit of independent scheduling and dispatch. Thread can be the kernel thread scheduled for the operating system kernel, such as Win32 Thread; user thread scheduled by user process, such as Linux Platform POSIX Thread;Or by the kernel and user processes, such as Windows 7 Threads for mixed scheduling.
Multiple threads in the same process will share all system resources in the process, such as virtual address space, file descriptor, signal processing, etc. However, multiple threads in the same process have their own call stacks( call stack),Own register environment( register context),Own thread local storage( thread-local storage). 
A process can have many threads, and each thread performs different tasks in parallel.
In multi-core or multi-core CPU,Or support Hyper-threading of CPU The advantage of using multithreaded programming on is obvious, that is, it improves the execution throughput of the program CPU On a single core computer, multithreading technology can also be responsible for the process I/O Processing, human-computer interaction and often blocked parts are executed separately from intensive computing parts, and special programs are written workhorse Threads perform intensive computation, which improves the execution efficiency of the program.
  • 1
  • 2
  • 3
  • 4
  • 5

There are two ways to create threads. The first way is to inherit QThread and then rewrite run, but this way is officially not recommended. We don't write this way if it is not recommended by the official. We're talking about the second way here.

Inherit QObject and move to a new thread.

Override QObject

// Header file
class workThread : public QObject
{
    Q_OBJECT
public:
    workThread(QObject* parent = nullptr);
    ~workThread();
public slots:
    void start1();
    void doWork();
signals:
    void workFinished();
    void workStart();
};

//cpp
workThread::workThread(QObject* parent) : QObject (parent)
{
}
workThread::~workThread()
{
}
void workThread::start1()
{
    emit workStart();
    doWork();
}
void workThread::doWork()
{
    for (int i = 0; i < 1000000; i++)
    {
        qDebug()<<i<<endl;
    }
    emit workFinished();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

usage method

 QThread* m_workerThread = new QThread();
    workThread* worker = new workThread();
    worker->moveToThread(m_workerThread);

    connect(m_workerThread, &QThread::started, worker, &workThread::start1);
    connect(worker, &workThread::workFinished, m_workerThread, &QThread::quit);
    connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);

    //You can also exit to release resources
//    connect(qApp, &QApplication::aboutToQuit, worker, &QObject::deleteLater);
//    connect(worker, &QObject::destroyed, m_workerThread, &QThread::quit);
//    connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

To sum up, such an operation interface is not stuck at all, because the delayed operation is put into a new thread.
If you need to transfer data, you can transfer the data through the signal slot.

The reason why the official does not recommend rewriting QThread is also because the signal slot cannot be used
If you want to inherit QThread, you can also. This class that inherits QThread also needs moveToThread. This method is not true, so you don't want to use it.

Logic 5 thread + timer

In fact, it is an advanced version of logic 4, plus a timer to output the current time every two seconds

class TimerThread : public QObject
{
    Q_OBJECT
public:
    TimerThread(QObject* parent = nullptr);
    ~TimerThread();
public:
    void run();
    void doWork();
signals:
    void workStart();
    void workFinished();
};

static int timerCount = 0;
TimerThread::TimerThread(QObject* parent): QObject (parent)
{
}
TimerThread::~TimerThread()
{
}
void TimerThread::run()
{
    emit workStart();
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &TimerThread::doWork);
    timer->start(2000);
}
void TimerThread::doWork()
{
    timerCount ++;
    if (timerCount > 100)
        emit workFinished();
    qDebug()<<QTime::currentTime()<<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

Business code here

 auto fTimerThreadStart = [=] () -> void
    {
        fiveThread->start();
    };
    connect(ui->threadButton2, &QPushButton::clicked, fTimerThreadStart);
    connect(fiveThread, &QThread::started, timerObject, &TimerThread::run);
    connect(timerObject, &TimerThread::workFinished, fiveThread, &QThread::quit);
    connect(fiveThread, &QThread::finished, fiveThread, &QThread::deleteLater);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

The interface is smooth and smooth. You can think about the specific business logic requirements again.

Posted by Ganlek on Sun, 31 Oct 2021 20:55:02 -0700