• 6516阅读
  • 0回复

[转载]Qt经典—线程与QObjects [复制链接]

上一主题 下一主题
离线zjhcool
 
只看楼主 倒序阅读 楼主  发表于: 2011-02-22
我们在上文中已经讨论了事件循环,我们可能理所当然地认为在Qt的应用程序中只有一个事件循环,但事实并不是这样:QThread对象在它们所代表的线程中开启了新的事件循环。因此,我们说main 事件循环是由调用main()的线程通过QCoreApplication::exec() 创建的。 它也被称做是GUI线程,因为它是界面相关操作唯一允许的进程。一个QThread的局部事件循环可以通过调用QThread::exec() 来开启(它包含在run()方法的内部)
class Thread : public QThread {
protected:
void run() {
/* ... initialize ... */
exec();
}
};



正如我们之前所提到的,自从Qt 4.4 的QThread::run() 方法不再是一个纯虚函数,它调用了QThread::exec()。就像QCoreApplication,QThread 也有QThread::quit() 和QThread::exit()来停止事件循环。
一个线程的事件循环为驻足在该线程中的所有QObjects派发了所有事件,其中包括在这个线程中创建的所有对象,或是移植到这个线程中的对象。我们说一个QObject的线程依附性(thread affinity)是指某一个线程,该对象驻足在该线程内。我们在任何时间都可以通过调用QObject::thread()来查询线程依附性,它适用于在QThread对象构造函数中构建的对象。
class MyThread : public QThread
{
public:
MyThread()
{
otherObj = new QObject;
}    
private:
QObject obj;
QObject *otherObj;
QScopedPointer<QObject> yetAnotherObj;
};



如上述代码,我们在创建了MyThread 对象后,obj, otherObj, yetAnotherObj 的线程依附性是怎么样的?要回答这个问题,我们必须要看一下创建他们的线程:是这个运行MyThread 构造函数的线程创建了他们。因此,这三个对象并没有驻足在MyThread 线程,而是驻足在创建MyThread 实例的线程中。
要注意的是在QCoreApplication 对象之前创建的QObjects没有依附于某一个线程。因此,没有人会为它们做事件派发处理。(换句话说,QCoreApplication 构建了代表主线程的QThread 对象)

我们可以使用线程安全的QCoreApplication::postEvent() 方法来为某个对象分发事件。它将把事件加入到对象所驻足的线程事件队列中。因此,除非事件对象依附的线程有一个正在运行的事件循环,否则事件不会被派发。
理解QObject和它所有的子类不是线程安全的(尽管是可重入的)非常重要;因此,除非你序列化对象内部数据所有可访问的接口、数据,否则你不能让多个线程同一时刻访问相同的QObject(比如,用一个锁来保护)。请注意,尽管你可以从另一个线程访问对象,但是该对象此时可能正在处理它所驻足的线程事件循环派发给它的事件! 基于这种原因,你不能从另一个线程去删除一个QObject,一定要使用QObject::deleteLater(),它会Post一个事件,目标删除对象最终会在它所生存的线程中被删除。(译者注:QObject::deleteLater作用是,当控制流回到该对象所依附的线程事件循环时,该对象才会被“本”线程中删除)。
此外,QWidget 和它所有的子类,以及所有与GUI相关的类(即便不是基于QObject的,像QPixmap)并不是可重入的。它们必须专属于GUI线程。
我们可以通过调用QObject::moveToThread()来改变一个QObject的依附性;它将改变这个对象以及它的孩子们的依附性。因为QObject不是线程安全的,我们必须在对象所驻足的线程中使用此函数;也就是说,你只能将对象从它所驻足的线程中推送到其他线程中,而不能从其他线程中拉回来。此外,Qt要求一个QObject的孩子必须与它们的双亲驻足在同一个线程中。这意味着:
你不能使用QObject::moveToThread()作用于有双亲的对象;
你千万不要在一个线程中创建对象的同时把QThread对象自己作为它的双亲。 (译者注:两者不在同一个线程中):
class Thread : public QThread {
void run() {
QObject obj = new QObject(this); // WRONG!!!
}
};



这是因为,QThread 对象驻足在另一个线程中,即QThread 对象它自己被创建的那个线程中。
Qt同样要求所有的对象应该在代表该线程的QThread对象销毁之前得以删除;实现这一点并不难:只要我们所有的对象是在QThread::run() 方法中创建即可。(译者注:run函数的局部变量,函数返回时得以销毁)。
跨线程的信号与槽
接着上面讨论的,我们如何应用驻足在其他线程里的QObject方法呢?Qt提供了一种非常友好而且干净的解决方案:向事件队列post一个事件,事件的处理将以调用我们所感兴趣的方法为主(当然这需要线程有一个正在运行的事件循环)。而触发机制的实现是由moc提供的内省方法实现的(译者注:有关内省的讨论请参见我的另一篇文章Qt的内省机制剖析):因此,只有信号、槽以及被标记成Q_INVOKABLE的方法才能够被其它线程所触发调用。


..
标签: QObject, Qt, QThread, 线程
本文链接: Qt经典—线程与QObjects
版权所有: Venus, 转载请注明来源Venus并保留链接地址!



上一篇: Qt经典—Qt 线程类

标签: QObject, Qt, QThread, 线程
本文链接: Qt经典—线程与QObjects
版权所有: Venus, 转载请注明来源Venus并保留链接地址!
相关文章






[ 此帖被zjhcool在2011-02-22 11:33重新编辑 ]
我的博客地址: http://newfaction.net
快速回复
限100 字节
 
上一个 下一个