QT面试必备知识 QObject QObject与多线程 QObject的依附线程 QObject是否是线程安全的 QThread QObject的线程依附性是否可以改变 如何安全的在另外一个线程中调用QObject对象的接口 事件 事件循环 notify sendEvent postEvent deleteLater 信号槽 信号重载了,如何确定连接哪个信号? 槽函数参数、信号的参数 槽函数的参数是否可以比信号的参数多?
QObject
完整看一遍:线程和 QObject
QObject与多线程
QObject的依附线程
QObject::thread()可以查询一个QObject的线程依附性。每一个QObject对象或者子对象,都拥有一个记录所依附线程的成员数据:QThreadData threadData,记录了拥有这个QObject对象的线程ID号。 一个QObject的所依附的线程(thread affinity)是指它所在的那个线程,即QObject对象创建的那个线程。 线程的事件循环会给依附在这个线程上的所有QOjbect对象派发事件。 Qt 要求QObject的所有子对象都必须和其父对象在同一线程。 因此不能在QThread中以这个QThread本身作为父对象创建对象,例如:
class Thread : public QThread {void run() {QObject *obj = new QObject(this); // 错误!}
};
QObject是否是线程安全的
QObject及其所有子类都不是线程安全的(但都是可重入的)。因此,你不能有两个线程同时访问一个QObject对象,除非这个对象的内部数据都已经很好地序列化(例如为每个数据访问加锁)。
QThread
这里有两个概念:
QThread对象所在的线程 —— 创建QThread对象所在的那个线程,而不是创建的那个子线程 QThread对象所管理的线程 —— 真正创建的那个子线程 线程的事件循环用于为线程中的所有QObjects对象分发事件;默认情况下,这些对象包括线程中创建的所有对象,或者是在别处创建完成后被移动到该线程的对象。
QObject的线程依附性是否可以改变
调用QObject::moveToThread()函数。该函数会改变一个对象及其所有子对象的线程依附性。
由于QObject本身是线程不安全的,因此moveToThread接口的调用必须在QObject对象所在的线程内调用。
如何安全的在另外一个线程中调用QObject对象的接口
QObject被设计成在一个单线程中创建与使用,因此,在一个线程中创建一个对象,而在另外的线程中调用它的函数,这样的行为不能保证工作良好。
使用信号槽的队列连接或者QT的反射系统提供的QMetaObject::invokeMethed的队列连接调用。这要求接口必须是内省的,也就是说这个函数要么是一个槽函数,要么标记有Q_INVOKABLE宏。 将事件提交到接收对象所在线程的事件循环;当事件发出时,响应函数就会被调用。
事件
事件循环
主事件循环
int QCoreApplication::exec()
主事件循环接收来自系统的事件,并转换为QT的事件消息,并把这些事件分发到对应的应用程序窗口上。
notify
bool QCoreApplication::notify(QObject *receiver, QEvent *event)
把事件发送到receiver的事件处理函数{receiver}->event(event)中 如果接收者receiver对event事件不感兴趣,事件则会传递给receiver的父对象,直到顶级对象的事件处理函数中。
5种处理事件的方法:
1、重写QObject的事件处理函数mousePressEvent 2、重写QCoreApplication::notify 拥有完整的控制 3、给QCoreApplication::instance()安装一个事件过滤器,因此事件过滤器里能处理所有控件的所有事件,控制强度跟notify一样。 4、重写QObject::event() 5、给一个QObject对象安装一个事件过滤器,这个事件过滤器则会处理这个QObject对象的所有事件。
sendEvent
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
使用notify函数,直接把事件给到接受者 event最好是栈空间,sendEvent不会删除event对象。
postEvent
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
把事件event投递到对象receiver所在事件队列中,并立即返回。
线程安全的QCoreApplication::postEvent()函数向一个对象发送事件。它将把事件加入到对象所在的线程的事件队列中,因此,如果这个线程没有运行事件循环,这个事件也不会被派发。 event必须在堆上分配,因为postEvent会获取event的所有权,并把原始的event删除。 当控制返回到主事件循环,那么所有存储在事件队列中的一个个事件将会被调用notify函数分发出去。 事件队列中的事件按事件优先级降序排序,即高优先级的事件在队头。 这个接口是线程安全的
deleteLater
对于deleteLater的用法,官方描述如下:
The object will be deleted when control returns to the eventloop. If the event loop is not running when this function iscalled (e.g. deleteLater() is called on an object beforeQCoreApplication::exec()), the object will be deleted once theevent loop is started. If deleteLater() is called after the main event loophas stopped, the object will not be deleted.Since Qt 4.8, if deleteLater() is called on an object that lives in athread with no running event loop, the object will be destroyed when thethread finishes.
实现如下:
void QObject::deleteLater()
{QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}
它是线程安全的删除QObject对象的方法。
分析下deleteLater调用后的过程 Aobj->deleteLater()相当于QCoreApplication::postEvent(Aobj, new QDeferredDeleteEvent());,把一个QDeferredDeleteEvent事件投递到了Aobj所依附的线程的事件队列中,等Aobj所依附的线程的事件循环获得控制权时,删除Aobj对象。
Qobj->deleteLater()调用后,Qobj这个对象真正被执行删除的时候是在(当前这个线程?)重新回到事件循环的时候处理的。如果在事件循环未启动前就调用deleteLater,那么这个对象会在事件循环一启动就会被删除。如果在事件循环结束后,再调用deleteLater,那么这个对象将不会被删除。 如果在一个子线程中,这个子线程没有运行事件循环,那么这个对象的删除,将在子线程结束时删除。而对于整个程序而言,主线程都会有事件循环(因为,return QApplication::exec()),所以主线程中删除对象不会存在这个情形。
一个对象调用deleteLater后,再调用它的接口,是否会有问题 不会有问题。不会立刻删除对象,直到进入事件循环
信号槽
参考:https://www.kancloud.cn/kancloud/qt-study-road-2/99456
信号重载了,如何确定连接哪个信号?
答,采用函数指针确定连接哪个信号。
槽函数参数、信号的参数
槽函数的参数可以少于信号的参数。
槽函数的参数是否可以比信号的参数多?
也可以。唯一的情况就是槽函数参数带有默认参数,除去默认参数外,槽函数的参数必须小于等于信号的参数。