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注意事项:
- 不要经常执行moveToThread。执行此动作时,m_worker中的所有运行中的timer会被停止,然后在新线程中restart,如果经常做会延迟timer。
- 槽函数在线程中运行。moveToThread之后m_worker中所有的槽函数会在新线程中运行。
- moveToThread不要滥用。moveToThread只能用来将对象从当前线程转移到另外的线程,不能用于将对象从别的线程转移到当前线程中。
参考官网:http://doc.qt.io/qt-5/qobject.html#moveToThread
测试代码:
https://github.com/RobinsonSir/QThreadTest1