gpt4 book ai didi

c++ - 有什么方法可以检测 QObject 是否属于 "dead"QThread?

转载 作者:行者123 更新时间:2023-11-30 03:39:42 25 4
gpt4 key购买 nike

故事:

我对应用程序中的每个“长”操作都使用 QtConcurrent API。它工作得很好,但我在创建 QObjects 时遇到了一些问题。

考虑这段代码,它使用一个线程来创建一个“Foo”对象:

QFuture<Foo*> = QtConcurrent::run([=]()
{
Data* data = /*long operation to acquire the data*/
Foo* result = new Foo(data);
return result;
});

它运行良好,但如果“Foo”类派生自 QObject 类,则“结果”实例属于创建该对象的 QThread。

所以要正确使用“结果”实例的信号/槽,应该做这样的事情:

QFuture<Foo*> = QtConcurrent::run([=]()
{
Data* data = /*long operation to acquire the data*/
Foo* result = new Foo(data);
// Move "result" to the main application thread
result->moveToThread(qApp->thread());
return result;
});

现在,一切都按预期工作,我认为这是正常行为和名义解决方案。

问题:

我有很多这种代码,有时创建对象,也可以创建对象。它们中的大多数都是通过“moveToThread”调用正确创建的。

但有时,我会错过一个“moveToThread”调用。

然后,很多事情看起来都不起作用(因为这个对象槽“坏了”),没有任何 Qt 警告。

现在,我有时会花很多时间来弄清楚为什么有些东西不起作用,然后才明白这只是因为不再在特定对象实例上调用插槽。

问题:

有什么办法可以帮助我预防/检测/调试这种情况吗?例如:

  • 每次删除 QThread 但仍有属于它的对象仍然存在时都会记录警告?
  • 每次向已删除 QThread 的对象发出信号时是否记录警告?
  • 每次向对象(在另一个线程中)发出信号但在超时前未处理时是否记录警告?

谢谢

最佳答案

跟踪对象在线程间的移动是可能的。就在一个对象被移动到新线程之前,它被发送一个 ThreadChange 事件。您可以过滤该事件并让您的代码运行以记录对象何时离开线程。但现在要知道该物体是否去了任何地方还为时过早。要检测到这一点,您需要将元调用(请参阅 this question)发布到对象的队列中,以便在对象的事件处理在新线程中恢复时立即执行。您还可以附加到 QThread::finished 以有机会查看您的对象列表并检查它们是否存在于即将结束的线程上。

但这一切都相当复杂:每个线程都需要自己的跟踪器/过滤器对象,因为事件过滤器必须存在于对象的线程中。我们可能需要 200 多行代码才能正确处理所有极端情况。

相反,您可以利用 RAII 并使用将线程关联作为资源管理的句柄来保存对象(因为它是一个资源!):

// https://github.com/KubaO/stackoverflown/tree/master/questions/thread-track-38611886
#include <QtConcurrent>

template <typename T>
class MainResult {
Q_DISABLE_COPY(MainResult)
T * m_obj;
public:
template<typename... Args>
MainResult(Args&&... args) : m_obj{ new T(std::forward<Args>(args)...) } {}
MainResult(T * obj) : m_obj{obj} {}
T* operator->() const { return m_obj; }
operator T*() const { return m_obj; }
T* operator()() const { return m_obj; }
~MainResult() { m_obj->moveToThread(qApp->thread()); }
};

struct Foo : QObject { Foo(int) {} };

您可以按值返回一个MainResult,但必须显式指定仿函数的返回类型:

QFuture<Foo*> test1() {
return QtConcurrent::run([=]()->Foo*{ // explicit return type
MainResult<Foo> obj{1};
obj->setObjectName("Hello");
return obj; // return by value
});
}

或者,您可以返回调用 MainResult 的结果;它本身是一个仿函数,可以节省一些输入,但这可能被认为是一种 hack,也许您应该将 operator()() 转换为具有短名称的方法。

QFuture<Foo*> test2() {
return QtConcurrent::run([=](){ // deduced return type
MainResult<Foo> obj{1};
obj->setObjectName("Hello");
return obj(); // return by call
});
}

虽然最好将对象与句柄一起构造,但也可以将实例指针传递给句柄的构造函数:

MainResult<Foo> obj{ new Foo{1} };

关于c++ - 有什么方法可以检测 QObject 是否属于 "dead"QThread?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38611886/

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