gpt4 book ai didi

c++ - 如何检测 Qt5 中的 QObject::moveToThread() 失败?

转载 作者:太空狗 更新时间:2023-10-29 20:12:36 27 4
gpt4 key购买 nike

QObject::moveToThread() 上的文档为 Qt5.3 解释说 moveToThread()如果对象有父对象,方法可能会失败。我如何在我的代码中检测到这个故障?

我意识到简单地确保我的对象首先没有父对象可能就足够了,但作为防御性编程实践,我想测试所有可能失败的调用的返回值。

编辑:我想在这里强调一些答案,我完全知道我可以在调用 moveToThread 之前测试 parent 是否为 0。我正在寻找可能的方法来凭经验确定 moveToThread调用居然成功了。

最佳答案

可靠地获得 moveToThread() 的结果, catch 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与另一个线程的父级。为此,您只需调用 moveToThread()QObject的根上你想要移动的层次结构,所有的 child 也会被移动(这是为了确保 parent 和他们的 child 总是在同一个线程上)。我知道,这是学术上的区别。只是在这里彻底。
  • moveToThread()QObject 时调用也可能失败的 thread()不是 == QThread::currentThread() (即,您只能将一个对象推到另一个线程上,而不能从另一个线程中拉出一个对象)。
  • 最后一句是lie-to-children .如果对象之前已与任何线程分离(通过调用 moveToThread(nullptr) .
  • ),您可以拉取该对象。
  • 当线程亲和性发生变化时,对象会被发送 QEvent::ThreadChange事件。

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

    为什么这是一个问题?

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

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

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

    您可以通过对移动到的对象进行子类化并重新实现 QObject::event() 来检查事件。或者通过在要移动的对象上安装事件过滤器。

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

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

    关于c++ - 如何检测 Qt5 中的 QObject::moveToThread() 失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26692649/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com