QThread编程示例
class MyThread: public QThread //创建线程类
{
protected:
void run() //线程入口函数
{
for(int i=0; i<5; i++)
{
qDebug() << objectName() << ":" << i;
sleep(1) //暂停1s
}
}
};
多线程编程初探
实例1:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
protected:
void run()
{
qDebug() << objectName() << " : " << "run() begin";
for(int i=0; i<5; i++)
{
qDebug() << objectName() << ": " << i;
sleep(1);
}
qDebug() << objectName() << " : " << "run() end";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main() begin";
MyThread t;
t.setObjectName("t");
t.start(); //创建一个线程,并执行线程体run函数
qDebug() << "main() end";
return a.exec();
}
示例中的主线程将先于子线程结束,所有线程都结束后,进程结束
实例2
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
protected:
void run()
{
qDebug() << objectName() << " : " << "run() begin";
for(int i=0; i<5; i++)
{
qDebug() << objectName() << ": " << i;
sleep(1);
}
qDebug() << objectName() << " : " << "run() end";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main() begin";
MyThread t;
t.setObjectName("t");
t.start(); //创建一个线程,并执行线程体run函数
MyThread tt;
tt.setObjectName("tt");
tt.start();
for(int i=0; i<100000; i++)
{
for(int j=0; j<10000; j++)
{
}
}
qDebug() << "main() end";
return a.exec();
}
第一次运行结果:
第二次运行结果
从上面的运行结果看,每次运行结果都不同。
在主线程中创建的两个子线程是并行执行的,这两个线程间没有交互,各自执行。这就是线程间的并行性
重要注意:
在工程开发中terminate是禁止使用的,terminate会使得操作系统暴力终止线程,而不考虑数据的完整性、资源释放等问题。
问题:如何在代码中优雅的终止线程
解决方案思路:
-run函数执行结束是优雅终止线程的唯一方式
-在线程类中增加标识变量m_toStop(volatile bool)每次都需要从内存中读取,不需要编译器做任何的优化。
-通过m_toStop的值判断是否需要从run函数返回
class Solution : public QThread
{
protected:
volatile bool m_toStop;
void run();
public:
Solution()
{
m_toStop = false;
}
void stop()
{
m_toStop = true;
}
};
void Solution::run()
{
while(!m_toStop)
{
//do task
}
}
首先看一下暴力终止线程的例子:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
protected:
void run()
{
qDebug() << objectName() << " : " << "run() begin";
for(int i=0; i<20; i++)
{
qDebug() << objectName() << ": " << i;
msleep(500);
}
qDebug() << objectName() << " : " << "run() end";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main() begin";
MyThread t;
t.setObjectName("t");
t.start(); //创建一个线程,并执行线程体run函数
for(int i=0; i<100000; i++)
{
for(int j=0; j<10000; j++)
{
}
}
t.terminate();
qDebug() << "main() end";
return a.exec();
}
在子线程中需要执行20次的,但当使用了terminate()暴力终止线程时,该线程出现了非正常死亡,该种方式不安全,也不优雅。
下面看一个暴力终止一个线程,带来内存泄露的问题。
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
protected:
void run()
{
qDebug() << objectName() << " : " << "run() begin";
int* p = new int[10000];
for(int i=0; i<20; i++)
{
qDebug() << objectName() << ": " << i;
msleep(500);
}
delete[] p;
qDebug() << objectName() << " : " << "run() end";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main() begin";
MyThread t;
t.setObjectName("t");
t.start(); //创建一个线程,并执行线程体run函数
for(int i=0; i<100000; i++)
{
for(int j=0; j<10000; j++)
{
}
}
t.terminate();
qDebug() << "main() end";
return a.exec();
}
在子线程中,申请了一片堆空间,定义了10000个int型的数据。我们知道,堆空间只要申请了,就必须释放。但是上面这个程序释放了吗?看样子是真的释放了,但事实真的如此吗?看一下打印结果:
从打印结果看,for循环只是运行了17次,就说明该子线程没有运行完,该线程就被暴力终止了。因此delete[] p肯定没有执行,申请了堆空间,没有被释放,那么将会产生内存泄露。
下面来看一下优雅的结束线程
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
protected:
volatile bool m_toStop;
void run()
{
qDebug() << objectName() << " : " << "run() begin";
int* p = new int[10000];
for(int i=0; (!m_toStop)&&(i<20); i++)
{
qDebug() << objectName() << ": " << i;
msleep(500);
}
delete[] p;
qDebug() << objectName() << " : " << "run() end";
}
public:
MyThread()
{
m_toStop = false;
}
void stop()
{
m_toStop = true;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main() begin";
MyThread t;
t.setObjectName("t");
t.start(); //创建一个线程,并执行线程体run函数
for(int i=0; i<100000; i++)
{
for(int j=0; j<10000; j++)
{
}
}
t.stop();
qDebug() << "main() end";
return a.exec();
}
使用t.stop优雅的结束线程,从打印结果看,t run() end被执行了,说明delete[] p被执行了,申请的堆空间被释放了。(注意,即使20次for循环没有被执行完,但是申请的堆空间被释放,达到了我们的要求)。
这种解决方案并不只适用Qt开发中,在其他的平台开发中,只要涉及多线程都可以考虑这种解决方案。