写技术文章好像还是好多年前的事了,记得那时候刚接触C#的时候写过几篇,之后就一直没有写过了,因为习惯性了用文件记录,然后放到网盘,需要时从网盘找。可能是因为文笔不好,反正就那么回事了。这次打算记录些东西,就先说说QThread的使用吧。
环境:VS2015 + Qt 5.12.5(应该只要是Qt5都可以)
QThread有两种用法:
1、写一个QObject子类,实例化之后,用moveToThread()将它移到新线程中,然后运行线程(推荐)
2、子类化一个QThread,然后实现run()虚函数
要用好QThread,首先要认识到QThread实际上只是一个线程管理的类,它并不是线程本身,所以它在创建它的线程空间里。下面分别看两个用法的示例。
用法1示例:
#include <QCoreApplication>
#include <QObject>
#include <QThread>
//为了直观就将代码和头文件放一起了
class ChildObj : public QObject
{
Q_OBJECT
public:
ChildObj(int value, QObject *parent = nullptr) : QObject(parent), mValue(value) {}
int Value(void) const { return mValue; }
private:
int mValue;
};
class ParentObj : public QObject
{
Q_OBJECT
public:
ParentObj(QObject *parent) : QObject(parent),
mChildA(10), //没有指定父对象,所以它属于调用这个方法时所在的线程(本例在主线程调用该方法,所以它属于主线程)
mChildB(11, this), //指定父对象为this(即:parentObj),而parentObj被移入子线程,所以它属于子线程
mChildC(new ChildObj(12)), //没有指定父对象,所以它属于调用这个方法时所在的线程(本例在主线程调用该方法,所以它属于主线程)
mChildD(new ChildObj(13, this)), //指定父对象为this(即:parentObj),而parentObj被移入子线程,所以它属于子线程
mChildE(nullptr),
mChildF(nullptr)
{
mChildE = new ChildObj(14); //没有指定父对象,所以它属于调用这个方法时所在的线程(本例在主线程调用该方法,所以它属于主线程)
mChildF = new ChildObj(15, this); //指定父对象为this(即:parentObj),而parentObj被移入子线程,所以它属于子线程
}
~ParentObj()
{
//如果线程正在运行,则退出,并等待完成退出
if(isRunning())
{
mStopFlag = true;
while(isRunning()) QThread::usleep(5);
}
}
void PrintMsg(void) { emit SigPrintMsg("This is a test"); }
void Stop(void) { mStopFlag = true; }
void CreateChildI(void) { mChildI = new ChildObj(18); }
void CreateChildJ(void) { mChildJ = new ChildObj(19, this); }
void Run()
{
mChildG = new ChildObj(16); //没有指定父对象,所以它属于调用这个方法时所在的线程(这个方法在子线程中运行,所以它属于子线程)
mChildH = new ChildObj(17, this); //指定父对象为this(即:parentObj),而parentObj被移入子线程,所以它属于子线程
mStopFlag = false;
while(!mStopFlag)
{
//在这里做一些想要做的事
}
emit SigFinished();
}
signals:
void SigFinished(void);
void SigPrintMsg(const QString &msg);
private:
bool mStopFlag;//是否退出标志
ChildObj mChildA; //本示例结果:主线程
ChildObj mChildB; //本示例结果:子线程
ChildObj *mChildC; //本示例结果:主线程
ChildObj *mChildD; //本示例结果:子线程
ChildObj *mChildE; //本示例结果:主线程
ChildObj *mChildF; //本示例结果:子线程
ChildObj *mChildG; //本示例结果:子线程
ChildObj *mChildH; //本示例结果:子线程
ChildObj *mChildI; //本示例结果:主线程
ChildObj *mChildJ; //本示例结果:主线程
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ParentObj *parentObj = new ParentObj;
QThread *thread = new QThread;
parentObj->moveToThread(&thread);//将parentObj移到子线程thread中
//连接信号和槽
connect(thread, &QThread::started, parentObj, &ParentObj::Run);//线程启动后自动执行ParentObj::Run()
//在使用过程中发现过一些奇怪的情况:
//使用connect(thread, &QThread::finished, thread, &QThread::deleteLater);有时候不会触发QThread::deleteLater
//使用connect(thread, &QThread::finished, [=]{thread.deleteLater();});每次都可以触发QThread::deleteLater
connect(thread, &QThread::finished, thread, &QThread::deleteLater);//线程退出后自动删除线程对象
connect(parentObj, &ParentObj::SigFinished, [=]{ delete parentObj; thread->quit(); });//通知其它模块退出线程
connect(parentObj, &ParentObj::SigPrintMsg, [=](const QString &msg){ qDebug("%s", msg.toStdString().c_str());});//打印出信息
thread->start();//必须运行线程,不运行的话本例中的ParentObj::Run()是不会执行的
//本例中,执行thread->start()之后会调用ParentObj::Run()
//但ParentObj::Run()如果很快就退出了
//此时再通过其它线程调用parentObj->PrintMsg(),则:
//connect(parentObj, &ParentObj::SigPrintMsg, [=](const QString &msg){ qDebug("%s", msg.toStdString().c_str());});这句不会被触发
//因为这个时候parentObj已经被移到thread中了,而thread线程已经退出了,无法再发出SigPrintMsg了
//主线程还是可以调用这些方法的
parentObj->CreateChildI(); //由主线程调用此方法,所以parentObj->mChildI在主线程
parentObj->CreateChildJ(); //由主线程调用此方法,但parentObj在子线程,所以它在主线程还是子线程?实测结果:主线程
QThread::usleep(5000);//延时几秒钟
thread->Stop();//退出线程
while(thread->isRunning()) QThread::usleep(10); //等待线程退出
//事件循环
return a.exec();
}
使用时需要注意:调用类里面的某个方法时控制权在哪个线程,则那个方法就是在哪个线程中执行,所以,如果子线程和主线程都要用到该对象,就需要使用锁!例如上例中的parentObj->CreateChildI()在主线程中执行;因为parentObj已经移动到子线程中,而且子线程已运行,所以ParentObj::Run()在子线程中执行。
另外,有些Qt对象(例如:QSerialPort、QTcpSocket、QUdpSocket)如果它存在的线程不处理好,会出现问题(这时候,在构造函数中初始化这类对象时要指定它的父对象为this!)
用法2示例:
#include <QCoreApplication>
#include <QObject>
#include <QThread>
//为了直观就将代码和头文件放一起了
class ChildThread : public QThread
{
Q_OBJECT
public:
ChildThread (QThread *parent = nullptr) : QThread(parent),
mValue_1(1), // mValue_1在主线程空间
mValue_2(new int), // mValue_1在主线程空间
mValue_3(nullptr)
{ }
~ChildThread()
{
//如果线程正在运行,则退出,并等待完成退出
if(isRunning())
{
mStopFlag = true;
while(isRunning()) QThread::usleep(5);
}
}
void Stop(void) { mStopFlag = true; }
void Run()
{
mValue_3 = new int;//mValue_3在子线程空间
mStopFlag = false;
while(!mStopFlag)
{
//在这里做一些想要做的事
}
emit SigFinished();
}
signals:
void SigFinished(void);
private:
bool mStopFlag;//是否退出标志
int mValue_1;
int *mValue_2;
int *mValue_3;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ChildThread *thread = new ChildThread;
connect(thread, &ChildThread::SigFinished, thread, QThread::deleteLater);//自动释放
thread->start();//必须运行线程,不运行的话本例中的ChildThread::Run()是不会执行的
QThread::usleep(5000);//延时几秒钟
thread->Stop();//退出线程
while(thread->isRunning()) QThread::usleep(10); //等待线程退出
//事件循环
return a.exec();
}
总的来说,这种用法只有run()才是在子线程中(即:在run()中创建的变量和指针才在子线程),其它都在主线程!
今天的文章quercetin的用法用量_qthread线程安全退出[通俗易懂]分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/78837.html