Qt QThread 探索(一)

Cannot create children for a parent that is in a different thread.

在Qt的官方文档,大家知道有两种方式使用 QThread。

  • You can use worker objects by moving them to the thread using QObject::moveToThread().
  • Another way to make code run in a separate thread, is to subclass QThread and reimplement run().

在使用MoveToThread这种方式时,经常会遇到下面类似的问题:

  • QObject: Cannot create children for a parent that is in a different thread.

出现这样的问题根本原因就是:
moveToThread之前声明的变量在父线程,之后的槽函数运行在新线程,其中声明的变量当然也属于新线程。

此例调用MoveToThread 之后,在 Worker的槽函数中Worker的私有成员中又进行了new操作,并且将this指针传给了构造函数。看下实例:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QThread>
#include <QMainWindow>

#include "worker.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

signals:
    void doWorkSignal();

private:
    Ui::MainWindow *ui;

    QThread m_thread;

    WorkerB m_worker;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_worker.moveToThread(&m_thread);

    connect(this, SIGNAL(doWorkSignal()),
            &m_worker, SLOT(doWork()));

    m_thread.start();

    emit doWorkSignal();

    qDebug() << "MainWin thread: " << QThread::currentThread();
}

MainWindow::~MainWindow()
{
    delete ui;

    m_thread.exit();
    m_thread.wait();
}

worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QThread>
#include <QNetworkAccessManager>

class WorkerA: public QObject
{
    Q_OBJECT
public:
    inline explicit WorkerA(QObject *parent = 0)
    {
        m_net1 = new QNetworkAccessManager(this);

        qDebug() << "Create WorkerA thread: " << QThread::currentThread();
    }

    inline void doWorkA()
    {
        m_net2 = new QNetworkAccessManager(this);

        qDebug() << "DoWorkA thread: " << QThread::currentThread();

        qDebug() << "Net1 Parent: " << m_net1->parent();
        qDebug() << "Net2 Parent: " << m_net2->parent();;
    }

    inline ~WorkerA()
    {
        delete m_net1;
        delete m_net2;
    }


private:
    QNetworkAccessManager *m_net1;
    QNetworkAccessManager *m_net2;
};

class WorkerB : public QObject
{
    Q_OBJECT
public:
    explicit WorkerB(QObject *parent = 0);
    ~WorkerB();

signals:

public slots:
    void doWork();

private:
    WorkerA *m_workerA;

};

#endif // WORKER_H


worker.cpp

#include <QDebug>

#include "worker.h"

WorkerB::WorkerB(QObject *parent) :
    QObject(parent)
{
    m_workerA = new WorkerA(this);

    qDebug() << "Create WorkerB thread: " << QThread::currentThread();
}

void WorkerB::doWork()
{  
    qDebug() << "DoWorkB thread: " << QThread::currentThread();

    m_workerA->doWorkA();
}

WorkerB::~WorkerB()
{
    //delete m_workerTimer;
}


程序运行输出:

Create WorkerA thread:  QThread(0x7aad48)
Create WorkerB thread:  QThread(0x7aad48)
MainWin thread:  QThread(0x7aad48)
DoWorkB thread:  QThread(0x60fe0c)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is WorkerA(0x7acdc0), parent's thread is QThread(0x7aad48), current thread is QThread(0x60fe0c)
DoWorkA thread:  QThread(0x60fe0c)
Net1 Parent:  WorkerA(0x7acdc0)
Net2 Parent:  QObject(0x0)

在本案例中,WorkerB在MainWindow的构造函数中初始化,WorkerB及WorkerA的parent指针都为MainWindow。
而我们使用信号调用WorkerB的doWoker()函数时,此函数会在另外一个线程(0x60fe0c)执行,

此时我们直接调用m_worker.doWork(),注意槽函数是在调用的线程运行的(而非子线程)

m_net2 = new QNetworkAccessManager(this);

这里的this显然是MainWindow,而MainWindow是在主线程中,故会报错

QObject: Cannot create children for a parent that is in a different thread.
(Parent is WorkerA(0x4558a8), parent's thread is QThread(0x4482e8), current thread is QThread(0x28fe1c)

解决办法是:
方法1 new时不传递this指针

m_net2 = new QNetworkAccessManager;

方法2 将new操作放在WorkerA的构造函数中

m_net1 = new QNetworkAccessManager(this);
m_net2 = new QNetworkAccessManager(this);

方法3 使用信号与槽的方法调用doWorkA()

#总结

QObject: Cannot create children for a parent that is in a different thread.

注意对象所在的线程问题。

moveToThread之前声明的变量在父线程,之后的槽函数运行在新线程,其中声明的变量当然也属于新线程。

使用moveToThread注意事项:

  1. 不要经常执行moveToThread。执行此动作时,m_worker中的所有运行中的timer会被停止,然后在新线程中restart,如果经常做会延迟timer。
  2. 槽函数在线程中运行。moveToThread之后m_worker中所有的槽函数会在新线程中运行。
  3. moveToThread不要滥用。moveToThread只能用来将对象从当前线程转移到另外的线程,不能用于将对象从别的线程转移到当前线程中。

参考官网:http://doc.qt.io/qt-5/qobject.html#moveToThread

测试代码:
https://github.com/RobinsonSir/QThreadTest1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值