【问题标题】:Any way to detect if a QObject belongs to a "dead" QThread?有什么方法可以检测 QObject 是否属于“死”QThread?
【发布时间】:2016-12-01 09:37:06
【问题描述】:

故事:

我对应用程序中的每个“长”操作都使用 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 的对象发出信号时都会记录警告?
  • 每次向对象(在另一个线程中)发出信号并且在超时前未处理时都会记录警告?

谢谢

【问题讨论】:

  • Foo 类型 QObjects 是否有理由必须具有非主线程亲和性(我认为您所说的“属于”是什么意思)?
  • 第三题重复;请删除它。您在问如何检测挂起的事件循环 - 这与是否发出信号无关。见here

标签: c++ qt qthread qobject qtconcurrent


【解决方案1】:

可以跟踪对象在线程之间的移动。就在一个对象被移动到新线程之前,它被发送一个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} };

【讨论】:

  • 在这种情况下,使用管理线程亲和性的句柄可能是最好的主意。谢谢。
猜你喜欢
  • 2011-10-13
  • 2013-01-18
  • 2010-10-28
  • 2010-11-04
  • 1970-01-01
  • 2019-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多