QThread 继承 QObject 。它发射指示线程启动 (或执行完成) 的信号,且还提供了几个槽。
更有趣的是 QObject 可以用于多线程,发射援引其它线程槽的信号,并把事件张贴给 "存活" 于其它线程中的对象。这是可能的,因为每个线程都允许拥有它自己的事件循环。
QObject 可重入。它的大多数非 GUI (图形用户界面) 子类,譬如 QTimer , QTcpSocket , QUdpSocket , QFtp ,和 QProcess ,也可重入,使之可能从多线程同时使用这些类。注意,设计是从单线程创建和使用这些类;在某个线程中创建对象并从另一线程调用其函数,不能保证能工作。要注意存在 3 个约束:
this
) 作为在线程中创建对象的父级 (由于
QThread
对象自身是在另一线程中创建的)。
尽管 QObject 是可重入 GUI 类,显而易见 QWidget 及其所有子类,都不可重入。只可以从主线程使用它们。如前所述, QCoreApplication::exec () 还必须从该线程调用。
在实践中,不可能在主线程外的其它线程中使用 GUI (图形用户界面) 类,通过把耗时操作放入单独工作者线程,并在工作线程完成时在主线程屏幕中显示结果,可轻松解决。此实现方式可以用于 Mandelbrot 和 阻塞 Fortune 客户端 范例。
Each thread can have its own event loop. The initial thread starts its event loops using QCoreApplication::exec (); other threads can start an event loop using QThread::exec ()。像 QCoreApplication , QThread 提供 exit (int) 函数和 quit() 槽。
线程中的事件循环使之可能对要使用某些非 GUI Qt 类的线程,要求存在事件循环 (譬如 QTimer , QTcpSocket ,和 QProcess )。还使之可能把来自任何线程的信号,连接到特定线程槽。阐述这的更多细节在 信号和槽跨线程 以下章节。
A QObject 实例据称是 live 在创建它的线程中。此对象的事件由该线程的事件循环分派。线程对于 QObject 存活的获得是使用 QObject::thread ().
Note that for QObjects that are created before QApplication , QObject::thread () returns zero. This means that the main thread will only handle posted events for these objects; other event processing is not done at all for objects with no thread. Use the QObject::moveToThread () function to change the thread affinity for an object and its children (the object cannot be moved if it has a parent).
调用
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 支持这些信号/槽连接类型:
注意: 使用这种类型连接同一线程中的对象,会导致死锁。
可以指定连接类型,通过把额外自变量传递给 connect() 。要意识到,当发送者和接收者活在不同线程中时 (若事件循环运行在接收者线程中) 使用直接连接不安全,出于相同原因,调用活在另一线程中对象的任何函数也不安全。
QObject::connect () 本身是线程安全的。
The Mandelbrot example uses a queued connection to communicate between a worker thread and the main thread. To avoid freezing the main thread's event loop (and, as a consequence, the application's user interface), all the Mandelbrot fractal computation is done in a separate worker thread. The thread emits a signal when it is done rendering the fractal.
同样, 阻塞 Fortune 客户端 example uses a separate thread for communicating with a TCP server asynchronously.