QThread 继承 QObject 。它发射指示线程启动 (或执行完成) 的信号,且还提供了几个槽。
更有趣的是 QObject 可以用于多线程,发射援引其它线程槽的信号,并把事件张贴给 "存活" 于其它线程中的对象。这是可能的,因为每个线程都允许拥有它自己的事件循环。
QObject 可重入。它的大多数非 GUI (图形用户界面) 子类,譬如 QTimer , QTcpSocket , QUdpSocket and QProcess ,也可重入,使之可能从多线程同时使用这些类。注意,设计是从单线程创建和使用这些类;在某个线程中创建对象并从另一线程调用其函数,不能保证能工作。要注意存在 3 个约束:
this
) 作为在线程中创建对象的父级 (由于
QThread
对象自身是在另一线程中创建的)。
尽管 QObject 是可重入 GUI 类,显而易见 QWidget 及其所有子类,都不可重入。只可以从主线程使用它们。如前所述, QCoreApplication::exec () 还必须从该线程调用。
在实践中,不可能在主线程外的其它线程中使用 GUI (图形用户界面) 类,通过把耗时操作放入单独工作者线程,并在工作线程完成时在主线程屏幕中显示结果,可轻松解决。此实现方式可以用于 Mandelbrot 范例 和 阻塞 Fortune 客户端范例 .
一般而言,创建 QObject 先于 QApplication 不支持,且退出时可能导致奇怪崩溃,从属平台。这意味着静态实例 QObject 也不支持。结构合理的单 (或多) 线程应用程序应该使 QApplication 被首先创造,且最后销毁 QObject .
每个线程都可以拥有它自己的事件循环。初始线程启动其事件循环是使用 QCoreApplication::exec (),或对于单对话框 GUI (图形用户界面) 应用程序,有时是 QDialog::exec ()。其它线程启动事件循环可以使用 QThread::exec ()。像 QCoreApplication , QThread 提供 exit (int) 函数和 quit() 槽。
线程中的事件循环使之可能对要使用某些非 GUI Qt 类的线程,要求存在事件循环 (譬如 QTimer , QTcpSocket ,和 QProcess )。还使之可能把来自任何线程的信号,连接到特定线程槽。阐述这的更多细节在 信号和槽跨线程 以下章节。
A QObject 实例据称是 live 在创建它的线程中。此对象的事件由该线程的事件循环分派。线程对于 QObject 存活的获得是使用 QObject::thread ().
The QObject::moveToThread () 函数改变对象,及其子级的线程亲缘关系 (对象无法移动,若它拥有父级)。
调用
delete
在
QObject
从线程而不是某个线程其
owns
对象 (或以其它方式访问对象) 是不安全的,除非保证对象在那刻不处理事件。使用
QObject::deleteLater
() 代替,和
DeferredDelete
事件将被张贴,最终将拾取对象线程的事件循环。默认情况下,线程
owns
a
QObject
是线程
creates
the
QObject
,但不后于
QObject::moveToThread
() 被调用。
若没有事件循环在运行,就不会将事件交付给对象。例如,若创建 QTimer 对象在线程,但从不调用 exec() , QTimer 将从不发射其 timeout() 信号。调用 deleteLater() 也不会工作 (这些限定也适用于主线程)。
可以在任何时间手动将事件张贴给任何线程中的任何对象,使用线程安全函数 QCoreApplication::postEvent ()。将通过创建对象线程的事件循环,自动分派事件。
支持事件过滤器的所有线程,具有监视对象必须活在如被监视对象的同一线程中的限定。同样, QCoreApplication::sendEvent () (不像 postEvent() ) 只可以用于把事件分派给活在从那里调用函数的线程的对象。
QObject 及其所有子类都不是线程安全的。这包括整个事件交付系统。它很重要,记住事件循环可能把事件交付给 QObject 子类,当从另一线程访问对象时。
若正调用函数在 QObject 子类未活在当前线程中且对象可能接收事件,就必须保护所有访问对 QObject 子类的内部数据按互斥;否则,可能经历崩溃 (或其它不期望行为)。
像其它对象, QThread 对象活在创建对象的线程中 -- not 在创建线程中,当 QThread::run () 被调用。提供槽通常不安全在 QThread 子类,除非采用互斥保护成员变量。
另一方面,可以安全地发射信号从 QThread::run () 实现,因为信号发出是线程安全的。
Qt 支持这些信号/槽连接类型:
注意: 使用这种类型连接同一线程中的对象,会导致死锁。
false
.
可以指定连接类型,通过把额外自变量传递给 connect() 。要意识到,当发送者和接收者活在不同线程中时 (若事件循环运行在接收者线程中) 使用直接连接不安全,出于相同原因,调用活在另一线程中对象的任何函数也不安全。
QObject::connect () 本身是线程安全的。
The Mandelbrot 范例 使用 "队列连接" 在工作者线程和主线程之间,进行通信。为避免冻结主线程的事件循环 (并因此,冻结应用程序的用户界面),所有 Mandelbrot 分形计算都是在单独工作者线程中完成。线程发射信号,当分形渲染完成时。
同样, 阻塞 Fortune 客户端范例 使用单独线程与 TCP 服务器进行异步通信。