QThread继承自QObject,它发射信号(signals)以表明线程执行开始或结束,并提供了一些槽函数(slots)。
更有趣的是,QObjects可以在多线程中使用,发射信号以在其它线程中调用槽函数,并且向“存活”于其它线程中的对象发送事件(post events)。这是可能的,因为每一个线程都拥有它自身的事件循环(event loop)。
简述
QObject可重入性
每个线程的事件循环
从其它线程访问QObject子类
跨线程的信号和槽
更多参考
QObject可重入性QObject是可重入的。它的大多数非GUI子类,例如:QTimer、QTcpSocket、QUdpSocket和QProcess,也都是可重入的,这使得在多线程中同时使用这些类成为可能。注意:这些类被设计成在单一线程中创建和使用的,在一个线程中创建一个对象而在另一个线程中调用该对象的函数,不保证能行得通。需要注意有三个约束:
一个QObject类型的孩子必须总是被创建在它的父亲所被创建的线程中。这意味着,除了别的以外,永远不要把QThread对象(this)作为该线程中创建的一个对象的父亲(因为QThread对象自身被创建在另外一个线程中)。
事件驱动的对象可能只能被用在一个单线程中。特别是,这适用于计时器机制(timer mechanism)和网络模块。例如:你不能在不属于这个对象的线程中启动一个定时器或连接一个socket,必须保证在删除QThread之前删除所有创建在这个线程中的对象。在run()函数的实现中,通过在栈中创建这些对象,可以轻松地做到这一点。
虽然QObject是可重入的,但GUI类,尤其是QWidget及其所有子类都不是可重入的,它们只能被用在主线程中。如前面所述,QCoreApplication::exec()必须也从那个线程被调用。
在实践中,只能在主线程而非其它线程中使用GUI的类这一点,可以很轻易地被解决:将耗时操作放在一个单独的worker线程中,当worker线程结束后在主线程中由屏幕显示结果。这个方法被用来实现Mandelbrot Example和the Blocking Fortune Client Example。
一般来说,在QApplication之前就创建QObject是不行的,会导致奇怪的崩溃或退出,取决于平台。这意味着,也不支持QObject的静态实例。一个单线程或多线程的应用程序应该先创建QApplication,并最后销毁QObject。
每个线程的事件循环每个线程都有它自己的事件循环。初始线程通过QCoreApplication::exec()来启动它的事件循环, 或者对于单对话框的GUI应用程序,有些时候用QDialog::exec(),其它线程可以用QThread::exec()来启动事件循环。就像QCoreApplication,QThread提供一个exit(int)函数和quit()槽函数.
一个线程中的事件循环使得该线程可以利用一些非GUI的、要求有事件循环存在的Qt类(例如:QTimer、QTcpSocket、和QProcess)。它也使得连接一些线程的信号到一个特定线程的槽函数成为可能。这一点将会在下面的“跨线程的信号和槽”有详细介绍。
一个QObject实例被称为存活于它所被创建的线程中。关于这个对象的事件被分发到该线程的事件循环中。可以用QObject::thread()方法获取一个QObject所处的线程。
QObject::moveToThread()函数改变一个对象和它的孩子的线程所属性。(如果该对象有父亲的话,它不能被移动到其它线程中)。
从另一个线程(不是该QObject对象所属的线程)对该QObject对象调用delete方法是不安全的,除非你能保证该对象在那个时刻不处理事件,使用QObejct::deleteLater()更好。一个DeferredDelete类型的事件将被提交(posted),而该对象的线程的事件循环最终会处理这个事件。默认情况下,拥有一个QObject的线程就是创建QObject的那个线程,而不是QObject::moveToThread()被调用后的。
如果没有事件循环运行,事件将不会传递给对象。例如:你在一个线程中创建了一个QTimer对象,但从没有调用exec(),那么,QTimer就永远不会发射timeout()信号,即使调用deleteLater()也不行。(这些限制也同样适用于主线程)。
利用线程安全的方法QCoreApplication::postEvent(),你可以在任何时刻给任何线程中的任何对象发送事件,这些事件将自动被分发到该对象所被创建的线程事件循环中。
所有的线程都支持事件过滤器,而限制是监控对象必须和被监控对象存在于相同的线程中。类似的,QCoreApplication::sendEvent()(不同于postEvent())只能将事件分发到和该函数调用者相同的线程中的对象。
从其它线程访问QObject子类