在另一个线程中创建一个 QObject 并将其检索到当前线程 = MSVC16 上的 Debug 中的 ASSERT 失败
Create a QObject in another thread and retrieve it to the current thread = ASSERT failure in Debug on msvc16
我只是想在另一个线程中创建的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 库,问题来自 setParent() 执行 sendEvent()
而不检查线程关联。这 2 个对象位于同一个线程中,但调用的 setParent() 是从另一个线程完成的。尽管我的操作不典型,但它仍然有效..这是一个简单的错误或者至少是一个未处理的案例。
根据调用线程,它应该执行 postevent()
。
最后我刚刚将 obj->setParent(&mainObj)
替换为
QMetaObject::invokeMethod(&mainObj, [&mainObj, obj](){
obj->setParent(&mainObj);
});
此调用是通过排队连接自动完成的,并最终在正确的线程中执行。当然我们必须在主线程中启动事件循环来检索这个排队的事件。
这是一个可接受的解决方法
#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, [&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
}
Even though my operation isn't typical, it's still valid.. It's a
simple bug or at least an unhandled case.
纯粹的废话。
Qt 中没有错误,错误在您的代码中。
obj 正在从 Thread::run 线程(即您的 lambda)实例化。
然后你把它移出这个线程(Thread::run线程),然后你就不能再直接和它通信了,因为线程安全......
QObject 是可重入的,不是线程安全的。
继续与此指针通信的唯一方法是通过同步,即使用排队的事件/连接,如 QMetaObject::invokeMethod、QTimer::singleShot 或发出连接到 obj 插槽的信号。
This is an acceptable workaround
没有。这是正确做事的唯一方法。
我只是想在另一个线程中创建的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 库,问题来自 setParent() 执行 sendEvent()
而不检查线程关联。这 2 个对象位于同一个线程中,但调用的 setParent() 是从另一个线程完成的。尽管我的操作不典型,但它仍然有效..这是一个简单的错误或者至少是一个未处理的案例。
根据调用线程,它应该执行 postevent()
。
最后我刚刚将 obj->setParent(&mainObj)
替换为
QMetaObject::invokeMethod(&mainObj, [&mainObj, obj](){
obj->setParent(&mainObj);
});
此调用是通过排队连接自动完成的,并最终在正确的线程中执行。当然我们必须在主线程中启动事件循环来检索这个排队的事件。
这是一个可接受的解决方法
#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, [&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
}
Even though my operation isn't typical, it's still valid.. It's a simple bug or at least an unhandled case.
纯粹的废话。
Qt 中没有错误,错误在您的代码中。 obj 正在从 Thread::run 线程(即您的 lambda)实例化。 然后你把它移出这个线程(Thread::run线程),然后你就不能再直接和它通信了,因为线程安全...... QObject 是可重入的,不是线程安全的。 继续与此指针通信的唯一方法是通过同步,即使用排队的事件/连接,如 QMetaObject::invokeMethod、QTimer::singleShot 或发出连接到 obj 插槽的信号。
This is an acceptable workaround
没有。这是正确做事的唯一方法。