Qt篇: 多线程如何创建之继承QThread
QThread是Qt的一个跨平台解决多线程方案,简单易学,可以让初学者很快上手。QThread是Qt里封装好的一个类,那么既然是类,那么继承QThread的线程就必须以对象的形式被创建和使用,每一个线程都应该对应一个对象。
主要成员函数
- run()函数:线程函数体,用于定义线程功能,该函数是线程的执行入口,将一切复杂耗时的业务处理放到该方法当中;
- start()函数:启动函数,将线程入口地址设置为run()函数的地址;
- terminate()函数:强制退出当前线程(不推荐使用),假设如果在线程当中分配了内存,使用该方法可能会导致在线程退出前,没有释放这块内存,就强制退出,这就会造成内存泄露,这也就是不推荐使用terminate()这个方法的根本原因。
QThread线程创建
- 继承QThread类,重写run()函数:
// MytThread.cpp
class MyThread : public QThread
{
protected:
void run()
{
qDebug() << "run() beging..."
for(int i; i<10; i++)
{
qDebug() << objectName() << ": " << i;
sleep(1);
}
qDebug() << "run() end..."
}
}
- 在主线程创建MyThread类实例,并调用start()方法,这就能完整的创建一个QThread的线程。
// main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main begin...";
MyThread t;
t.start();
qDebug() << "main end...";
return a.exec();
}
terminate初体验
在工程开发中,terminate()函数是不推荐使用,或者是禁止使用的!terminate()会使得操作系统暴力停止线程,而不会考虑数据的完整性和资源释放等问题。
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
protected:
void run()
{
qDebug() << "run() begin...";
// 申请内存
int *ret = new int[10000];
for( int i=0; i<10; i++ )
{
qDebug() << objectName() << ": " << i;
sleep(1);
ret[i] = i*i;
}
qDebug() << "delete ret...";
delete []ret;
qDebug() << "run() end...";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main begin...";
MyThread t;
t.setObjectName("t");
t.start();
QThread::sleep(5);
t.terminate();
qDebug() << "main end...";
return a.exec();
}
运行结果:
从结果来看,程序输出结果并没有打印"delete ret,,,"和"run() end...",可想而知在terminate强制退出后,ret所指向的内存并没有得到释放,造成内存泄露。而且这类产生的bug估计很难察觉到,毕竟很难想到会在主线程里使用terminate()进行强制退出,而造成的内存泄露。
如何优雅的退出线程
- run()函数执行结束是优雅终止线程的唯一方式(不仅仅局限于Qt的多线程);
- 在线程中增加标志变量m_toStop(volatile bool);
- 通过判断m_toStop的值判断是否需要从run()函数返回。
为什么要用volatile修饰m_toStop?
volatile关键字告诉编译器这个变量的值可能会在程序的其他地方发生变化,编译器不要对它进行优化。这在多线程编程中非常重要,因为编译器在优化代码时,可能会将某些变量的值缓存到寄存器中,从而导致其他线程对该变量的修改无法及时反映出来。 具体来说,使用volatile修饰m_toStop有以下好处:
- 防止编译器优化:确保每次在循环中检查m_toStop时,都会从内存中读取最新的值,而不是使用寄存器中的缓存值;
- 保证变量可见性:对于多线程环境,使用volatile可以保证线程对该变量的修改对其他线程是可见的,从而确保线程能够及时响应停止信号。
完整代码:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
volatile bool m_toStop;
public:
MyThread()
{
m_toStop = false;
}
// 将标志位设置为true,并优雅终止线程run()函数
void Stop()
{
m_toStop = true;
}
protected:
void run()
{
qDebug() << "run() begin...";
// 申请内存
int *ret = new int[10000];
for( int i=0; ((!m_toStop) && (i<10)); i++ )
{
qDebug() << objectName() << ": " << i;
sleep(1);
ret[i] = i*i;
}
qDebug() << "delete ret...";
delete []ret;
qDebug() << "run() end...";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main begin...";
MyThread t;
t.setObjectName("t");
t.start();
QThread::sleep(5);
// 调用Stop()方法
t.Stop();
qDebug() << "main end...";
return a.exec();
}