quercetin的用法用量_qthread线程安全退出[通俗易懂]

quercetin的用法用量_qthread线程安全退出[通俗易懂]写技术文章好像还是好多年前的事了,记得那时候刚接触C#的时候写过几篇,之后就一直没有写过了,因为习惯性了用文件记录,然后放到网盘,需要时从网盘找

quercetin的用法用量_qthread线程安全退出[通俗易懂]"

写技术文章好像还是好多年前的事了,记得那时候刚接触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

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注