Qt processes and threads: two ways to start threads

Qt provides a platform-independent QThread class for thread support. Multi-threaded programming can also effectively solve the problem of performing a time-consuming operation without freezing an application's user interface. Corresponding to the content of this section, you can view the Thread Support in Qt keyword in the help.

Here we are going to introduce the common functions of QThread and two ways to start threads:

  • subclassing
  • Use worker-objects to move them into threads via QObject::moveToThread

1. QThread common functions

Commonly used functions can be classified as follows according to their functions:

  • thread start
    • void start()
    • After the call, the run() function will be executed, but the signal started() will be emitted before the run() function is executed, and the operating system will schedule threads according to the priority parameters. If the thread is already running, then this function does nothing. The effect of the priority parameter depends on the scheduling policy of the operating system.
  • thread execution
    • int exec()
    • Each thread can have its own event loop, which can be started by calling the exec() function.
    • void run()
    • The starting point of the thread. After calling start(), the newly created thread will call this function. The default implementation calls exec(). Most of them need to reimplement this function to manage their own threads. After the function returns, the thread execution ends, just as the application leaves the main() function.
  • thread exits
    • void quit()
    • Make the thread exit the event loop and return 0 to indicate success, which is equivalent to calling QThread::exit(0).
    • void exit(int returnCode = 0)
    • Causes the thread to exit the event loop, returning 0 for success and any non-zero value for failure.
    • void terminate()
    • In extreme cases, you may want to forcibly terminate an executing thread, then you can use the terminate() function. However, the thread may or may not be terminated immediately, depending on the scheduling strategy of the operating system. Use QThread::wait() after terminate() to ensure nothing goes wrong.
    • Warning: Using the terminate() function, the thread may be terminated at any time and cannot perform some cleaning work, so this function is very dangerous, and it is generally not recommended to use it, only used when absolutely necessary.
    • void requestInterruption()
    • Qt5 newly introduces an interface to request thread interruption to close the thread. This request is 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.
  • thread wait
    • void msleep(unsigned long msecs) [static]
    • Force the current thread to sleep for msecs milliseconds.
    • void sleep(unsigned long secs) [static]
    • Force the current thread to sleep for secs seconds.
    • void usleep(unsigned long usecs) [static]
    • Force the current thread to sleep for usecs microseconds.
    • bool wait(unsigned long time = ULONG_MAX)
    • The thread will be blocked, waiting for time milliseconds. Unlike sleep, wait will return if the thread exits.
  • thread state
    • bool isFinished() const
    • Determine whether the thread is terminated
    • bool isRunning() const
    • Determine whether the thread is running
    • bool isInterruptionRequested() const
      Returns true if the task running on the thread should stop; you can use requestInterruption() to request an interruption. Newly introduced in Qt5 are interfaces for cleanly interrupting long-running tasks. It is safe to never check or act on the function return value, but it is recommended to do so frequently in long-running functions. Note: Do not call too frequently to keep overhead low. The sample program is as follows:
    • void run() { // Whether to request termination while (!isInterruptionRequested()) { // Time-consuming operation} }
  • thread priority
    • 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.

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

Enumerate QThread::Priority:

constant

value

describe

QThread::IdlePriority

0

Scheduled only when no other threads are running

QThread::LowestPriority

1

Less frequent scheduling than LowPriority

QThread::LowPriority

2

Less frequent scheduling than NormalPriority

QThread::NormalPriority

3

The default priority of the operating system

QThread::HighPriority

4

Scheduling more frequently than NormalPriority

QThread::HighestPriority

5

Scheduling more frequently than HighPriority

QThread::TimeCriticalPriority

6

Schedule as often as possible

QThread::InheritPriority

7

Use the same priority as the created thread. This is the default

2. Start the thread by subclassing QThread

A QThread represents a thread that can be controlled independently in the application. It shares data with other threads in the process, but executes independently. Compared with the general program, the execution starts from the main() function, and the QThread starts from the main() function. By default, run() starts the event loop by calling exec(). To create a thread, you need to subclass QThread and reimplement the run() function. For example:

class MyThread : public QThread
{
protected:
    void run();
};

void MyThread::run()
{
    QTcpSocket socket;
	...
    socket connectToHost(hostName, portNumber);
    exec();
}

This will create a QTcpSocket in a thread, and then execute the thread's event loop. You can create an instance of the thread externally, and then call the start() function to start executing the thread, and start() will call the run() function by default. When returning from the run() function, the thread execution ends.

Note that it is not possible to use any widget class in a thread.

example program

Let's look at an example of starting a thread in a graphical interface program. There are two buttons on the interface, one is used to start a thread, and the other is used to close the thread. Create a new Qt Gui application with the name myThread, the class name Dialog, and select QDialog as the base class. After completion, enter the design mode, put two Push Button buttons in the interface, change the display text of the first button to "start thread", change the name to startButton; change the display text of the second button to "terminate thread", change the name to stopButton, and uncheck its enabled attribute. Then add a new C++ class to the project, set the class name to "MyThread", set the base class to "QThread", and select "Inherited from QObject" for the type information. After completion, enter the mythread.h file and modify it as follows:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = 0);
    void stop();

protected:
    void run();

private:
    volatile bool stopped;
};

#endif // MYTHREAD_H

Here the stopped variable uses the volatile keyword, which keeps it up to date at all times, thereby avoiding errors when accessing it in multiple threads. Then enter the mythread.cpp file and modify it as follows:

#include "mythread.h"
#include <QDebug>

MyThread::MyThread(QObject *parent) :
    QThread(parent)
{
    stopped = false;
}

void MyThread::run()
{
    qreal i = 0;
    while (!stopped) 
    {
        qDebug() << QString("in MyThread: %1").arg(i);
        msleep(1000);
        i++;
    }
    stopped = false;
}

void MyThread::stop()
{
    stopped = true;
}

(1) The stopped variable is initialized to false in the constructor.

(2) The value of the stopped variable is always judged in the run() function. As long as it is false, the string with the value of i incrementing is printed every second.

(3) Set the stopped variable to true in the top() function, so that the loop in the run() function can be ended, and thus exit from the run() function, so that the entire thread ends. The stopped variable is used here to achieve the termination of the process. The dangerous terminate() function is not used, and the quit(), wait() and requestInterruption() functions are not used in the destructor.

The following uses a custom thread in the Dialog class. First go to the dialog.h file, add the header file "mythread.h", and then add the private object:

#include "mythread.h"
...
private:    
	MyThread thread;

Next go to the design mode, enter the slots corresponding to the click signals of the two buttons respectively, and change them as follows:

// 启动线程按钮
void Dialog::on_startButton_clicked()
{
    thread.start();
    ui->startButton->setEnabled(false);
    ui->stopButton->setEnabled(true);
}

// 终止线程按钮
void Dialog::on_stopButton_clicked()
{
    if (thread.isRunning())
    {
        thread.stop();
        ui->startButton->setEnabled(true);
        ui->stopButton->setEnabled(false);
    }
}

The start() function is called when the thread is started, and then the state of the two buttons is set. When terminating the thread, first use isRunning() to determine whether the thread is running, if so, call the stop() function to terminate the thread, and change the state of the two buttons. Now run the program, click the "Start Thread" button, view the output of the application output column, and then press the "Terminate Thread" button, you can see that the output has stopped.

Three, worker-object way to start the thread

Another way to create threads is to use worker-objects to move them into threads via QObject::moveToThread. For example:

//Worker类:在线程中执行的类,例如定时器超时操作
class Worker类 : public QObject
{
    Q_OBJECT

public slots:
    void doWork(const QString &parameter) 
    {
        QString result;
        // 这里是阻塞的操作
        emit resultReady(result);
    }

signals:
    void resultReady(const QString &result);
};

//Controller类:线程所在的类
class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
    
public:
    Controller() 
    {
        Worker *worker = new Worker;
        //将worker对象的线程从主线程移动到workerThread
        worker->moveToThread(&workerThread);
        //当workerThread线程结束后,会自动销毁该线程
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        
        //当Controller类发射了operate()信号,会调用worker对象的doWork槽函数
        connect(this, &Controller::operate, worker, &Worker::doWork);
        //当workerr类发射了resultReady()信号,会调用Controller对象的handleResults槽函数
        connect(worker, &Worker::resultReady, this, &Controller::handleResults);
        
        //最开始,启动workerThread线程
        workerThread.start();
    }
    
    ~Controller() 
    {
    	//在析构函数中终止进程
    	workerThread.requestInterruption();
        workerThread.quit();
        workerThread.wait();
    }
    
public slots:
    void handleResults(const QString &);
    
signals:
    void operate(const QString &);
};

In this way, the code in the Worker slot will be executed in a separate thread. In this way, some time-consuming operations can be easily completed in a separate worker thread. Any signal of any object in any thread can be associated with the slot of Worker. It is safe to associate signals and slots between different threads.

Note: In addition, if you have time, you can refer to the blog Qt-Threads and Timers to learn more about the two ways to start threads.

Fourth, close the thread

There are many ways to close a thread, here is one of the most common methods: use the quit(), wait() and requestInterruption() functions in the destructor. The sample program is as follows:

~WorkerThread() 
{
	// 请求终止
	requestInterruption();
	quit();
	wait();
}

The article is transferred from the blog garden (fengMisaka): Qt process and thread two: two ways to start the thread

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

Guess you like

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