Qt use multithreading experience – 1. Inherit QThread multithreading use method
Quit, exit, and Terminate functions. 2.3 Terminate a thread correctly. 2.4 How to start a thread correctly 2.4.1 Correctly Starting a global thread (and a thread where the UI always exists) 2.4.2 How to Start a local thread (a releasable thread) 3. Some summary of inheriting QThread
QObject to achieve multiple wire method is used to see: blog.csdn.net/czyt1988/ar… 1. Abstract Qt has two methods of multithreading, one of which is to inherit the run function of QThread, the other is to transfer a class inherited from QObject to a Thread. Before Qt4.8, the run method inherited from QThread was used, but after Qt4.8, Qt officially recommends using the second method. There is little difference between the two methods and they are both relatively convenient to use, but the method of inheriting QObject is more flexible. It is important to document how to properly create a thread and, in particular, how to properly exit a thread.
This article first introduces the common usage of QThread, which is probably covered in many articles on the web. If you are familiar with this usage, you can skip this section. This article focuses on several methods of thread exit, and the correct creation and exit of thread.
Before using the run method that inherits QThread, you need to know one rule:
Only the run function of QThread is in the new thread. All other functions are in the thread generated by QThread
For qthreads, only the run function is in the new thread and only the run function is in the new thread
Say important things 3 times!!
If a QThread is created in the same thread as the UI, all non-run functions of the QThread are the same as those of the UI thread. Therefore, other functions of the QThread’s descendant classes should avoid time-consuming operations and ensure that all time-consuming operations are performed in the RUN function. Calling a non-run function on a QThread is the same as calling a normal function on the UI thread. In this case, if the function changes a variable of the QThread that is also used in the run function, This is where you need to pay attention to locking, because this variable may have been called in run a few milliseconds ago and then changed by another thread.
2.1 write an inheritance in the QThread thread the focus of this paper is not teach you to inherit the run write a multi-threaded, any programming basis of five minutes to learn to use the QThread methods, this article really speak is behind the sections, such as how to safely exit a thread, how to open a temporary thread, running end immediately switched off and other issues. If you have some basic knowledge of QThreads, you can skip this section, but you might want to take a look at some of the questions raised later in the section.
Any thread that inherits from QThread implements multithreading by inheriting from QThread’s run function. Therefore, it must override QThread’s run function to write complex logic into QThread’s run function.
Consider an example of a common inherited QThread:
#ifndef THREADFROMQTHREAD_H
#define THREADFROMQTHREAD_H
#include <QThread>
class ThreadFromQThread : public QThread
{
Q_OBJECT
signals:
void message(const QString& info);
void progress(int present);
public:
ThreadFromQThread(QObject* par);
~ThreadFromQThread(a);void setSomething(a);
void getSomething(a);
void setRunCount(int count);
void run(a);
void doSomething(a);
private:
int m_runCount;
};
#endif // THREADFROMQTHREAD_H
Copy the code
The CPP file:
```cpp
#include "ThreadFromQThread.h"
#include <QDebug>
ThreadFromQThread::ThreadFromQThread(QObject* par) : QThread(par)
,m_runCount(20)
{
}
ThreadFromQThread::~ThreadFromQThread()
{
qDebug() << "ThreadFromQThread::~ThreadFromQThread()";
}
void ThreadFromQThread::setSomething()
{
msleep(500);
QString str = QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId());
emit message(str);
}
void ThreadFromQThread::getSomething()
{
msleep(500);
emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}
void ThreadFromQThread::setRunCount(int count)
{
m_runCount = count;
emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}
void ThreadFromQThread::run()
{
int count = 0;
QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
emit message(str);
while(1)
{
sleep(1);
++count;
emit progress(((float)count / m_runCount) * 100);
emit message(QString("ThreadFromQThread::run times:%1").arg(count));
doSomething();
if(m_runCount == count)
{
break;
}
}
}
void ThreadFromQThread::doSomething()
{
msleep(500);
emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}
Copy the code
This simple example has the usual contents of Qt classes, including ordinary methods, signal slots, and a run function. Here the function setSomething(); A 500ms delay is performed, as is getSomething. This is to verify that calling QThread member functions outside of QThread::run() will not run in the new thread.
This code uses QThread::currentThreadId(), which is a static function that returns a handle to the current thread.
In order to verify this thread, write a simple interface, this interface is mainly used to verify the following questions:
Call setSomething() in the UI thread; Functions and getSomething (); Does the function stall? Will calling QThread::quit() or QThread::exit() on the UI thread stop the thread? Does calling QThread:: Terminate on the UI thread stop the thread? How to exit a thread correctly? To verify the above, a simple interface is written as shown in the figure below:
```cpp
#include "Widget.h"
#include "ui_Widget.h"
#include "ThreadFromQThread.h"
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// Control initialization
ui->progressBar->setRange(0.100);
ui->progressBar->setValue(0);
ui->progressBar_heart->setRange(0.100);
ui->progressBar_heart->setValue(0);
// Button signal slot association
connect(ui->pushButton_qthread1,&QPushButton::clicked
,this,&Widget::onButtonQThreadClicked);
connect(ui->pushButton_qthread1_setSomething,&QPushButton::clicked
,this,&Widget::onButtonQthread1SetSomethingClicked);
connect(ui->pushButton_qthread1_getSomething,&QPushButton::clicked
,this,&Widget::onButtonQthread1GetSomethingClicked);
connect(ui->pushButton_qthreadQuit,&QPushButton::clicked
,this,&Widget::onButtonQthreadQuitClicked);
connect(ui->pushButton_qthreadTerminate,&QPushButton::clicked
,this,&Widget::onButtonQthreadTerminateClicked);
connect(ui->pushButton_qthreadExit,&QPushButton::clicked
,this,&Widget::onButtonQThreadExitClicked);
connect(ui->pushButton_doSomthing,&QPushButton::clicked
,this,&Widget::onButtonQThreadDoSomthingClicked);
connect(ui->pushButton_qthreadRunLocal,&QPushButton::clicked
,this,&Widget::onButtonQThreadRunLoaclClicked);
//
connect(ui->pushButton_qobjectStart,&QPushButton::clicked
,this,&Widget::onButtonObjectMove2ThreadClicked);
connect(ui->pushButton_objQuit,&QPushButton::clicked
,this,&Widget::onButtonObjectQuitClicked);
//
connect(&m_heart,&QTimer::timeout,this,&Widget::heartTimeOut);
m_heart.setInterval(100);
// Create a global thread
m_thread = new ThreadFromQThread(this);
connect(m_thread,&ThreadFromQThread::message
,this,&Widget::receiveMessage);
connect(m_thread,&ThreadFromQThread::progress
,this,&Widget::progress);
connect(m_thread,&QThread::finished
,this,&Widget::onQThreadFinished);
m_heart.start(a); } Widget::~Widget()
{
qDebug() < <"start destroy widget";
m_thread->stopImmediately(a);// Since the parent object of this thread is a Widget, a judgment is required when exiting
m_thread->wait(a);delete ui;
qDebug() < <"end destroy widget";
}
void Widget::onButtonQThreadClicked(a)
{
ui->progressBar->setValue(0);
if(m_thread->isRunning())
{
return;
}
m_thread->start(a); }void Widget::progress(int val)
{
ui->progressBar->setValue(val);
}
void Widget::receiveMessage(const QString &str)
{
ui->textBrowser->append(str);
}
void Widget::heartTimeOut(a)
{
static int s_heartCount = 0;
++s_heartCount;
if(s_heartCount > 100)
{
s_heartCount = 0;
}
ui->progressBar_heart->setValue(s_heartCount);
}
void Widget::onButtonQthread1SetSomethingClicked(a)
{
m_thread->setSomething(a); }void Widget::onButtonQthread1GetSomethingClicked(a)
{
m_thread->getSomething(a); }void Widget::onButtonQthreadQuitClicked(a)
{
ui->textBrowser->append("m_thread->quit() but not work");
m_thread->quit(a); }void Widget::onButtonQthreadTerminateClicked(a)
{
m_thread->terminate(a); }void Widget::onButtonQThreadDoSomthingClicked(a)
{
m_thread->doSomething(a); }void Widget::onButtonQThreadExitClicked(a)
{
m_thread->exit(a); }void Widget::onQThreadFinished(a)
{
ui->textBrowser->append("ThreadFromQThread finish");
}
Copy the code
The interface provides buttons for several of the issues mentioned above. The interface has a heartbeat progress bar, which is the timer control of the main program and is triggered every 100ms to prove that the MAIN program’s UI thread is not stuck. The second progress bar is controlled by the thread.
Clicking on the “QThread Run “button triggers the onButtonQThreadClicked slot, and the child thread will run. When the child thread runs, it will print
… /QtThreadTest/ThreadFromQThread.cpp->run,thread id:2900388672
The child thread is a loop, and each loop will print a message:
ThreadFromQThread: : run times: 1 doSomething – >… / QtThreadTest ThreadFromQThread. CPP, thread id: 2900388672 ThreadFromQThread: : run times: 2 doSomething – >… /QtThreadTest/ThreadFromQThread.cpp,thread id:2900388672
DoSomething is called in the run function, whose thread ID is 2900388672, so the doSomething function is running in the child thread.
At this time, I in interface click getSomething setSomething, doSomething will print:
GetSomething – >… / QtThreadTest ThreadFromQThread. CPP, thread id: 3021526784 setSomething – >… / QtThreadTest ThreadFromQThread. CPP, thread id: 3021526784 doSomething – >… /QtThreadTest/ThreadFromQThread.cpp,thread id:3021526784
If you call a member function of QThread from a non-run function, you are not running it in a thread.
When I click quit, nothing is done to the QThread. The exit function and quit function are useless without calling exec().
m_thread->quit() but not work
Click terminate and the thread terminates immediately, printing:
ThreadFromQThread finish
The dynamic diagram is as follows:
Therefore, you can see that neither quit nor exit terminates a thread in mid-stream. To terminate a thread immediately, you can use terminate, but this function is very volatile and is not recommended. So how do you safely terminate a thread?
2.3 The simplest way to terminate a thread correctly is to add a bool variable and modify the bool variable through the main thread to terminate the thread, but this may cause access conflicts and require locking. We need to add the following statement to the original header file:
#include <QMutex>
class ThreadFromQThread : public QThread
{
...........
public slots:
void stopImmediately(a);
private:
QMutex m_lock;
boolm_isCanRun; . };Copy the code
The run function needs to be modified:
void ThreadFromQThread::stopImmediately(a)
{
QMutexLocker locker(&m_lock);
m_isCanRun = false;
}
void ThreadFromQThread::run(a)
{
int count = 0;
m_isCanRun = true;// The tag can run
QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((unsigned int)QThread::currentThreadId());
emit message(str);
while(1)
{
sleep(1);
++count;
emit progress(((float)count / m_runCount) * 100);
emit message(QString("ThreadFromQThread::run times:%1").arg(count));
doSomething(a);if(m_runCount == count)
{
break;
}
{
QMutexLocker locker(&m_lock);
if(! m_isCanRun)// In each loop to determine whether it can run, if not, exit the loop
{
return; }}}}Copy the code
QMutexLocker can safely use QMutex in case you forget to unlock it (similar to STD ::unique_ptr), so that each loop will see if it needs to terminate immediately. The stopImmediately() function can be called externally to terminate a thread when it needs to exit immediately. As you can see from the previous example, since functions that call QThread but not run() on the main thread run on the main thread, A call on the main thread like m_thread->stopImmediately() will almost immediately set the thread member variable m_isCanRun to false(think procedure-oriented for multithreading), Therefore, if m_isCanRun is encountered in the run function of the child thread, it will exit the run function, and the function inherited from QThread will be regarded as finished after running the run function, and will send the Finish signal.
There are several ways to start a thread, which are designed to address the problem of owning its parent object, and how to delete it. The first thing to figure out is whether the thread is consistent with the lifetime of the UI and doesn’t end until the UI ends, or whether the thread is just created temporarily and destroyed when the computation is done.
In the first case, a thread is created with the thread-generating form as its parent object, so that the form terminates automatically destructing the thread’s object. If you don’t handle this, you may find that closing the window will cause your application to crash. Often this thread is a monitoring thread, such as monitoring a port. For the sake of distinction, I’ll call this a global thread, and it exists throughout the lifetime of the UI.
The second case is a kind of temporary thread, the thread is generally suddenly have to deal with a large calculation, in order not to let the UI suspended animation need to trigger the thread, then need to pay attention to a problem, is done in thread haven’t calculation, users suddenly stop or change how to deal with, this kind of thread often see more and more prone to errors, such as opening a large file, show a big picture, The user may look at a large image before the image processing is complete and then switch to the next image, how to handle the drawing thread to solve the problem smoothly? For the sake of distinction, we’ll call this a local thread, and it will only fire at some point in the life of the UI, and then it will be destroyed.
This comes down to the question of how to terminate a thread of execution!
I have found that most tutorials on the web teach you to create a global thread, but this thread is usually not used much, and it is easier to manage, need to pay attention to the application exit thread handling issues. Declare a thread pointer in the UI header file
widget.h:
ThreadFromQThread* m_thread;
1
wodget.cpp:
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget(a);private slots:
void onButtonQThreadClicked(a);
void onButtonQthread1SetSomethingClicked(a);
void onButtonQthread1GetSomethingClicked(a);
void onButtonQthreadQuitClicked(a);
void onButtonQthreadTerminateClicked(a);
void onButtonQThreadDoSomthingClicked(a);
void onQThreadFinished(a); .void progress(int val);
void receiveMessage(const QString& str);
void heartTimeOut(a);
private: Ui::Widget *ui; ThreadFromQThread* m_thread; QTimer m_heart; .Copy the code
Look at the forms-generated constructor first
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
,m_objThread(NULL)
{
ui->setupUi(this);
// Control initialization
ui->progressBar->setRange(0.100);
ui->progressBar->setValue(0);
ui->progressBar_heart->setRange(0.100);
ui->progressBar_heart->setValue(0);
// Button signal slot association
connect(ui->pushButton_qthread1,&QPushButton::clicked
,this,&Widget::onButtonQThreadClicked);
connect(ui->pushButton_qthread1_setSomething,&QPushButton::clicked
,this,&Widget::onButtonQthread1SetSomethingClicked);
connect(ui->pushButton_qthread1_getSomething,&QPushButton::clicked
,this,&Widget::onButtonQthread1GetSomethingClicked);
connect(ui->pushButton_qthreadQuit,&QPushButton::clicked
,this,&Widget::onButtonQthreadQuitClicked);
connect(ui->pushButton_qthreadTerminate,&QPushButton::clicked
,this,&Widget::onButtonQthreadTerminateClicked);
connect(ui->pushButton_doSomthing,&QPushButton::clicked
,this,&Widget::onButtonQThreadDoSomthingClicked);
// Heartbeat association
connect(&m_heart,&QTimer::timeout,this,&Widget::heartTimeOut);
m_heart.setInterval(100);
// Create a global thread
// Global threads can be created with window Pointers as parent objects
m_thread = new ThreadFromQThread(this);
// Signals and slots associated with threads
connect(m_thread,&ThreadFromQThread::message
,this,&Widget::receiveMessage);//
connect(m_thread,&ThreadFromQThread::progress
,this,&Widget::progress);
connect(m_thread,&QThread::finished
,this,&Widget::onQThreadFinished);
// The UI heartbeat starts
m_heart.start(a); }Copy the code
The thread is created when the form is created. The parent object of the thread can be set as the form. Do not manually delete the thread pointer. The QThread used for you is in Qt’s event loop, and manual delete can cause unexpected accidents. In theory, all QObjects should not be manually deleted. Without multithreading, manual delete might not be a problem, but with multithreading it can be very problematic because it is possible that the object you want to delete is queued in the Qt event loop and you have already deleted it outside. The program will crash.
If you do delete, see void QObject::deleteLater () [slot] this slot is very useful, especially for local threads. It will be used a lot later for safely terminating threads.
The thread is started by calling the start function where it is needed.
void Widget::onButtonQThreadClicked(a)
{
ui->progressBar->setValue(0);
if(m_thread->isRunning())
{
return;
}
m_thread->start(a); }Copy the code
If the thread is already running, you don’t actually do anything when you call start repeatedly.
A global thread is as simple as that, just start it when you need it. The real concern is how to safely exit the thread at the end of the UI.
The destructor in the widget should look like this:
Widget::~Widget()
{
qDebug() < <"start destroy widget";
m_thread->stopImmediately(a); m_thread->wait(a);delete ui;
qDebug() < <"end destroy widget";
}
Copy the code
M_thread ->wait(); M_thread destrioy the main thread destrioy the main thread destrioy the main thread destrioy the main thread destrioy the main thread destrioy the main thread destrioy the main thread destrioy Therefore, wait is to suspend until the child thread terminates.
There is also a way for qthreads to delete themselves by binding the parent object to **void QObject::deleteLater () [slot]**. M_thread ->wait() is omitted during widget destructor; This sentence.
Void QObject::deleteLater () [slot] void QObject::deleteLater () [slot] This slot function is key to safely releasing thread resources (it is not safe to delete thread Pointers directly).
Here’s a simple example:
void Widget::onButtonQThreadRunLoaclClicked(a)
{
// create a local thread
ThreadFromQThread* thread = new ThreadFromQThread(NULL);// The parent object is specified as NULL
connect(thread,&ThreadFromQThread::message
,this,&Widget::receiveMessage);
connect(thread,&ThreadFromQThread::progress
,this,&Widget::progress);
connect(thread,&QThread::finished
,this,&Widget::onQThreadFinished);
connect(thread,&QThread::finished
,thread,&QObject::deleteLater);// Call deleteLater after the thread terminates to destroy the allocated memory
thread->start(a); }Copy the code
This example is the same as before, but with a difference:
new ThreadFromQThread(NULL); Connect (Thread,&QThread::finished, Thread,&QObject::deleteLater); DeleteLater is called after the thread terminates to destroy the allocated memory. After the finished message is sent, the deleteLater function is called. After confirming that no object for this thread is found in the message loop, the deleteLater function is destroyed. However, we should pay attention to avoid the situation of repeatedly clicking the button to call the thread repeatedly. For some requirements, clicking the button after the thread is opened will not generate the thread again, and can only click the button again until the current thread is finished executing. This situation is very easy to handle, and can be achieved by adding a mark, and is generally less used.
A more common requirement is to click the button again to terminate the thread that did not execute last time and start a new thread. This kind of situation is very rare, for example, a common image browser, there will be the next figure and a picture on this button, the browser to load images are generally in the thread execution (or click on the big picture browser will similar card dead state), when the user clicks on one picture need to terminate the current image is loading, loading the next picture. You can’t require the client to load the next image until the current one is loaded, it’s almost single-threaded. At this point, you need to terminate the current thread and start a new thread to load the next image.
Now, the top function is going to look something like this
The UI header file requires a member variable to record which thread is running
privateSlots:void onLocalThreadDestroy(QObject* obj);
private:
QThread* m_currentRunLoaclThread;
Copy the code
Running a function that generates temporary threads becomes
void Widget::onButtonQThreadRunLoaclClicked(a)
{
// create a local thread
if(m_currentRunLoaclThread)
{
m_currentRunLoaclThread->stopImmediately(a); } ThreadFromQThread* thread =new ThreadFromQThread(NULL);
connect(thread,&ThreadFromQThread::message
,this,&Widget::receiveMessage);
connect(thread,&ThreadFromQThread::progress
,this,&Widget::progress);
connect(thread,&QThread::finished
,this,&Widget::onQThreadFinished);
connect(thread,&QThread::finished
,thread,&QObject::deleteLater);// Call deleteLater after the thread terminates to destroy the allocated memory
connect(thread,&QObject::destroyed,this,&Widget::onLocalThreadDestroy);
m_currentRunLoaclThread = thread;
thread->start(a); }void Widget::onLocalThreadDestroy(QObject *obj)
{
if(m_currentRunLoaclThread == obj)
{
m_currentRunLoaclThread = NULL; }}Copy the code
QObject:: Destroyed thread pointer is set to NULL. QObject:: Destroyed thread pointer is set to NULL. Notify UI to set m_currentRunLoaclThread to NULlPTR during thread object destructor;
- Some summary of inheriting QThread
QThread only the run function is in the new thread
What happens if QThread starts again before the run function is completed?
The answer is: nothing happens, QThread continues to execute its run function, and run is not called again. While calling start while the thread is not finished does nothing, it is advisable to exercise caution before starting:
void Widget::onButtonQThreadClicked(a)
{
ui->progressBar->setValue(0);
if(m_thread->isRunning())
{
return;
}
m_thread->start(a); }Copy the code
This call method is probably familiar to anyone who knows QThread
QThread does not exit a run in the middle of a thread because you called quit. The correct way to exit a thread is described above. What is the use of quit? This will be shown in the next chapter. This function is useful when executing multiple threads using the moveToThread method.
When the program exits, it determines whether each thread has quit, and if it hasn’t quit, it should terminate. If it doesn’t decide, it’s more likely to crash when the program exits. If the parent object of the thread is a window object, then in the destructor of the form, you also need to call the wait function to wait for the thread to complete the following destructor.
Because you cannot predict which statement will be executed next in a multi-threaded environment, locking and automatic deletion are useful tools. Locking is a trade-off between efficiency and security. Qt’s signal slot system can deal with these problems more effectively.