3

我的应用程序使用自定义序列化机制。

阶段 1 ) 该机制在单独的线程上加载数据集群,在那里创建所有适当的对象,依此类推。

阶段 2 ) 一旦它们全部被完全反序列化,它将它们交给主应用程序线程并在那里完成序列化,例如在对象之间建立连接等。

这个机制是我框架的一个新的补充——在此之前我在反序列化主线程上的所有东西。

我遇到的问题是在反序列化过程中创建的一些对象是 Qt 对象(基本上是一堆小部件)。它们都记录了创建它们的线程的 ID。当“阶段 2”的时间到来时,这些对象开始抱怨它们都不属于这个线程,因此不能发送任何信号等。

所以我在 QObject 上找到了一个方法,叫做“moveToThread”。不过,这个小虫子并不是很有帮助,因为它会执行检查并防止将对象从不同的线程移动到当前线程(为什么 scuh 是一个约束,我不知道)。

有谁知道我该怎么做?我可以保证这些对象只会在单独的线程上创建,并且从那时起,它们都将在主线程上运行和运行。

谢谢, 帕克萨斯

4

1 回答 1

4

当涉及到多线程时,必须小心处理 QObject 的实例或 QObject 子类的任何实例。查看此页面以了解该主题的介绍。

假设您的应用程序由两个线程AB组成。假设线程 A创建了一个对象 QObject实例

据说实例存在于线程 A 中。这意味着: - 它可以直接向线程 A 中的所有其他对象发送和接收信号。 - 从线程 A 对 myInstance 方法的所有调用都是线程安全的

另一方面,从另一个线程 B访问实例: - 不是线程安全的(您必须注意竞争条件)。- 此外,由实例发出并连接到线程 B 中对象插槽的信号不是直接的:通过复制所有方法参数并将所有内容放在要由线程 B 事件队列执行的事件中,插槽执行被推迟到稍后的时刻。

鉴于此,您可以利用的解决方案如下。

SubClass 是 QObject 的子类。

class SubClass : public QObject{
Q_OBJECT
[...]
}

线程 A 将运行以下方法来反序列化并使用 SubClass 实例填充内存

void methodA(){  /this method is executed by thread A

 QThread* threadB; //a reference to the QThread related to thread B
 [...]
 MyQObject* instance = someFactoryMethod();

 //we push the ownership of instance from thrad A to thread B
 instance->moveToThread( threadB ); 

 //do something else

}

但是请注意,如果线程 A 需要对instance执行其他操作,这可能还不够。特别是线程 A 可能会触发 MyQObject 中定义的一些信号。这是不允许的,因为现在线程 A 不再是实例的所有者。

在这种情况下,您需要推迟此类操作并要求线程 B 执行它。这是通过使用QMetaObject::invokeLater方法实现的。

InvokeLater 允许您调用特定的槽,要求线程 B 执行它。

假设 ClassInB 是一个类,其实例在线程 B 中使用

class ClassInB : public QObject{
 Q_OBJECT

public:
    [...]
slots:
  void complexOperation(MyQObject* o){
    [...]
    emitSomeSignal();
  }
signals:

  void someSignal();
}

在将实例移动到线程 B 之后,我们需要对存在于线程 B 中的 ClassInB 的实例执行complexOperation(),反过来,它也会发出 someSignal()。

void methodA(){  //this method is executed by thread A
  QThread* threadB; //a reference to the QThread related to thread B
  ClassInB* instanceOnB;   //a reference to a ClassInB instance living in thread B
  [...]

   MyQObject* instance = someFactoryMethod();

   //we push the ownership of instance from thread A to thread B
   instance->moveToThread( threadB ); 

   //we ask thread B to perform some operation related to instance
   QMetaObject::invokeLater(instanceOnB, "complexOperation", Q_ARG(MyQObject*, instance) );

}

为了能够使用 MyQObject* 作为invokeLater的参数,我们需要将它注册到 Meta 框架。您将需要:

  • 添加Q_DECLARE_METATYPE(MyQObject*)定义 MyQObject 的 .cpp
  • 在使用机制之前调用一次(例如在主中),qRegisterMetaType<MyQObject*>();
于 2013-07-11T15:18:34.233 回答