当涉及到多线程时,必须小心处理 QObject 的实例或 QObject 子类的任何实例。查看此页面以了解该主题的介绍。
假设您的应用程序由两个线程A和B组成。假设线程 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*>();