c – 如何在Qt5中检测QObject :: moveToThread()失败?
Qt5.3的
QObject::moveToThread()文档解释了如果对象有父对象,moveToThread()方法可能会失败.我如何在代码中检测到这个失败?
我意识到只是确保我的对象没有父对象可能已经足够好了,但作为防御性编程实践,我想测试可能失败的所有调用的返回值. 编辑:我想在一些答案之后强调一下,我完全清楚我可以在调用moveToThread之前测试父是否为0.我正在寻找可行的方法来根据经验确定moveToThread调用实际上是成功的. 解决方法
要可靠地获取moveToThread()的结果,请捕获正在进行移动的对象的ThreadChange事件(通过重写QObject :: event()或安装事件过滤器),并存储是否在对本地的引用中看到该事件变量:
static bool moveObjectToThread(QObject *o,QThread *t) { class EventFilter : public QObject { bool &result; public: explicit EventFilter(bool &result,QObject *parent = nullptr) : QObject(parent),result(result) {} bool eventFilter(QObject *,QEvent *e) override { if (e->type() == QEvent::ThreadChange) result = true; return false; } }; bool result = false; if (o) { o->installEventFilter(new EventFilter(result,o)); o->moveToThread(t); } return result; } 很长的故事: >文档错了.您可以将带有父项的QObject移动到另一个线程.为此,您只需要在要移动的QObject层次结构的根上调用moveToThread(),并且也将移动所有子项(这是为了确保父项及其子项始终位于同一线程上).我知道这是学术上的区别.只是在这里彻底. 现在,您的问题是如何可靠地检测到移动的发生.答案是:这并不容易.显而易见的第一件事是,将moveToThread()调用后的QObject :: thread()返回值与moveToThread()的参数进行比较并不是一个好主意,因为QObject :: thread()不是(记录为)线程-safe(参见the implementation). 为什么这是一个问题? 一旦moveToThread()返回,移动到的线程可能已经开始执行“对象”,即.该对象的事件.作为该处理的一部分,可以删除该对象.在这种情况下,对原始线程上的QObject :: thread()的以下调用将取消引用已删除的数据.或者新线程将对象移交给另一个线程,在这种情况下,原始线程中对thread()的调用中的成员变量的读取将与针对moveToThread()中的相同成员变量的写入竞争新线程. 底线:从原始线程访问moveToThread()ed对象是未定义的行为.不要这样做. 前进的唯一方法是使用ThreadChange事件.在检查完所有故障情况之后发送该事件,但是,至关重要的是,仍然来自原始线程(参见the implementation;如果实际上没有发生线程更改,发送这样的事件也是完全错误的). 您可以通过继承移动到的对象并重新实现QObject :: event()或在要移动的对象上安装事件过滤器来检查事件. 当然,事件过滤器方法更好,因为您可以将它用于任何QObject,而不仅仅是那些您可以或想要子类化的QObject.但是有一个问题:一旦事件被发送,事件处理就会切换到新线程,因此事件过滤器对象将从两个线程中敲定,这绝不是一个好主意.简单的解决方案:使事件过滤器成为要移动的对象的子项,然后它将随之移动.另一方面,这会给您提供如何控制存储生命周期的问题,这样即使移动的对象在到达新线程时立即被删除,您也可以获得结果.简而言之:存储需要是旧线程中变量的引用,而不是被移动对象的成员变量或事件过滤器.然后对存储的所有访问都来自原始线程,并且没有比赛. 但是,但……不是仍然不安全吗?是的,但仅当对象再次移动到另一个线程时.在这种情况下,事件过滤器将从第一个移动到的线程访问存储位置,并且将与来自原始线程的读访问权竞争.简单的解决方案:在事件过滤器触发一次后卸载它.这个实现留给读者一个练习:) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |