加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 如何在Qt5中检测QObject :: moveToThread()失败?

发布时间:2020-12-16 09:54:59 所属栏目:百科 来源:网络整理
导读:Qt5.3的 QObject::moveToThread()文档解释了如果对象有父对象,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(),并且也将移动所有子项(这是为了确保父项及其子项始终位于同一线程上).我知道这是学术上的区别.只是在这里彻底.
>当QObject的thread()不是== QThread :: currentThread()时,moveToThread()调用也会失败(即你只能将一个对象推送到另一个线程,但不能从另一个线程中拉出一个).
>最后一句是lie-to-children.如果之前已经与任何线程分离(通过调用moveToThread(nullptr)),则可以拉出一个对象.
>当线程关联性发生变化时,会向对象发送一个QEvent :: ThreadChange事件.

现在,您的问题是如何可靠地检测到移动的发生.答案是:这并不容易.显而易见的第一件事是,将moveToThread()调用后的QObject :: thread()返回值与moveToThread()的参数进行比较并不是一个好主意,因为QObject :: thread()不是(记录为)线程-safe(参见the implementation).

为什么这是一个问题?

一旦moveToThread()返回,移动到的线程可能已经开始执行“对象”,即.该对象的事件.作为该处理的一部分,可以删除该对象.在这种情况下,对原始线程上的QObject :: thread()的以下调用将取消引用已删除的数据.或者新线程将对象移交给另一个线程,在这种情况下,原始线程中对thread()的调用中的成员变量的读取将与针对moveToThread()中的相同成员变量的写入竞争新线程.

底线:从原始线程访问moveToThread()ed对象是未定义的行为.不要这样做.

前进的唯一方法是使用ThreadChange事件.在检查完所有故障情况之后发送该事件,但是,至关重要的是,仍然来自原始线程(参见the implementation;如果实际上没有发生线程更改,发送这样的事件也是完全错误的).

您可以通过继承移动到的对象并重新实现QObject :: event()或在要移动的对象上安装事件过滤器来检查事件.

当然,事件过滤器方法更好,因为您可以将它用于任何QObject,而不仅仅是那些您可以或想要子类化的QObject.但是有一个问题:一旦事件被发送,事件处理就会切换到新线程,因此事件过滤器对象将从两个线程中敲定,这绝不是一个好主意.简单的解决方案:使事件过滤器成为要移动的对象的子项,然后它将随之移动.另一方面,这会给您提供如何控制存储生命周期的问题,这样即使移动的对象在到达新线程时立即被删除,您也可以获得结果.简而言之:存储需要是旧线程中变量的引用,而不是被移动对象的成员变量或事件过滤器.然后对存储的所有访问都来自原始线程,并且没有比赛.

但是,但……不是仍然不安全吗?是的,但仅当对象再次移动到另一个线程时.在这种情况下,事件过滤器将从第一个移动到的线程访问存储位置,并且将与来自原始线程的读访问权竞争.简单的解决方案:在事件过滤器触发一次后卸载它.这个实现留给读者一个练习:)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读