问题描述
我只是想在另一个线程中创建的QObject上设置父对象(对象当然已经移到了父线程之前),仅此而已!
#include <QApplication>
#include <QDebug>
#include <QThread>
class Thread : public QThread //Just a convenient Class using a lambda
{
public:
Thread::Thread(QObject *parent = nullptr) : QThread(parent){}
std::function<void()> todo;
protected:
virtual void run() override{
todo();
}
};
int main(int argc,char *argv[])
{
QApplication a(argc,argv);
///////////////////////////////////////////////
// Change this flag to switch behavIoUrs
bool tryToSetParentInThread = true;
///////////////////////////////////////////////
QObject mainObj;
QObject *dummy; //Just to get back obj created in thread;
Thread thread;
thread.todo = [&](){
//QObject *obj = new QObject(&mainObj); //"QObject: Cannot create children for a parent that is in a different thread." ! Of course!
// So we try this
QObject *obj = new QObject;
dummy = obj;
qDebug()<<obj->thread();
obj->movetoThread(mainObj.thread());
qDebug()<<obj->thread(); //Check that the Thread affinity change is done
if(tryToSetParentInThread)
obj->setParent(&mainObj);
QObject::connect(obj,&QObject::destroyed,[](){ //Parent mecanism is OK
qDebug()<<"Child destroyed";
});
};
thread.start();
thread.wait();
if(!tryToSetParentInThread)
dummy->setParent(&mainObj);
return 0; //No need for an event loop here
}
编辑:
也许obj->setParent(&mainObj)
的调用不喜欢mainObj不在调用方法..的线程中?
此示例在发行版中运行良好,但是如果您尝试使用mscv16在调试中启动此代码,则:
解决方法
所以我调试了qt lib,问题出在setParent()做sendEvent()
而没有检查线程亲和力。这两个对象位于同一线程中,但是调用的setParent()是从另一个对象完成的。即使我的操作不是典型操作,它仍然有效。这是一个简单的错误,或者至少是一个未处理的情况。
取决于调用线程,它应该执行postevent()
。
最后,我刚刚将obj->setParent(&mainObj)
替换为
QMetaObject::invokeMethod(&mainObj,[&mainObj,obj](){
obj->setParent(&mainObj);
});
此调用自动通过队列连接完成,最后在正确的线程中执行。当然,我们必须在主线程中启动eventloop才能检索此排队的事件。
这是可以接受的解决方法
#include <QApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>
class Thread : public QThread //Just a convenient Class using a lambda
{
public:
Thread::Thread(QObject *parent = nullptr) : QThread(parent){}
std::function<void()> todo;
protected:
virtual void run() override{
todo();
}
};
int main(int argc,char *argv[])
{
QApplication a(argc,argv);
QObject mainObj;
Thread thread;
thread.todo = [&](){
//QObject *obj = new QObject(&mainObj); //"QObject: Cannot create children for a parent that is in a different thread." ! Of course!
// So we try this
QObject *obj = new QObject;
qDebug()<<obj->thread();
obj->moveToThread(mainObj.thread());
qDebug()<<obj->thread(); //Check that the Thread affinity change is done
QMetaObject::invokeMethod(&mainObj,obj](){
obj->setParent(&mainObj);
});
QObject::connect(obj,&QObject::destroyed,[](){ //Parent mecanism is OK
qDebug()<<"Child destroyed";
});
};
thread.start();
thread.wait();
QTimer::singleShot(0,[](){
qApp->quit();
});
return a.exec(); //Add an event loop for the connect (queued) from the thread -> setParent() triggers a SendEvent() in the main thread where the two object now live
}
,
即使我的操作不是典型的,它仍然有效。 简单的错误或至少未解决的情况。
胡说八道。
Qt中没有错误,该错误在您的代码中。 obj是从Thread :: run线程(即您的lambda)实例化的。 然后将其移出此线程(Thread :: run线程),然后就无法再与其直接通信,因为线程安全... QObject是可重入的,不是线程安全的。 继续与该指针通信的唯一方法是通过同步,即使用排队的事件/连接,例如QMetaObject :: invokeMethod,QTimer :: singleShot或发出连接到obj插槽的信号。
这是可以接受的解决方法
不。这是正确做事的唯一方法。