QT元对象系统(下)

发布于 2021-09-20  87 次阅读


QT中的信号传递

书接上文

上文我们了解了QT的反射机制的底层原理,也就是利用MOC生成额外代码,接下来就是如何利用这些特性,进行信号与槽的连接。

同样从例子出发

当我们使用emit发射信号的时候,实际上只是调用了这个函数,我们可以看到这个信号函数的实现:

    // SIGNAL 0  
    void TestObject::clicked()  
    {  
        QMetaObject::activate(this, &staticMetaObject, 0, 0);  
    }  
    // SIGNAL 1  
    void TestObject::pressed()  
    {  
        QMetaObject::activate(this, &staticMetaObject, 1, 0);  
    }  

其调用了QMetaObject的方法,并传入了this指针,并且附带上了自己的 QMetaObject 对象的引用。后头跟的参数大概是信号的索引和参数之类的。

所以信号的传递和 QMetaObject 息息相关。自然而然的就能够想到,connect函数实际上操作的是 QMetaObject 。

代码太长咱不看

直接讲原理吧。一个对象的信号的发送会传递 this指针 以及这个对象的 QMetaObject 。

connect函数实际上就是在sender对象的QMetaObject中创建了一个列表,其中包含receiver对象的指针,以及对应的槽函数签名(字符串)。

在发送信号的时候,sender对象会去遍历这个列表,拿到 receiver对象的指针 ,调用其meta_call,根据其函数签名就能成功调用啦!

稍微讲讲跨线程

QT的信号可以连接到不同线程里的槽函数,这就涉及到connect的类型:

  1. Qt::AutoConnection:自动(默认)。信号发射对象如果和槽的执行对象在同一个线程,将是直连方式,否则就是队列方式。
  2. Qt::DirectConnection:直连。信号一旦发射,槽立即执行,并且槽是在信号发射的线程中执行的。(同一线程是同步执行)
  3. Qt::QueuedConnection:队列。信号发射后当事件循环返回到接收线程时槽函数就执行了,也就是说这种连接方式不是立即触发槽函数的,而是要排队等的,并且是在槽函数的线程中执行。(不同线程是异步执行)
  4. Qt::BlockingQueuedConnection:阻塞队列。在槽函数返回之前信号所在的线程都是阻塞的。(当信号与槽在同一个线程里就会导致死锁)
  5. Qt::UniqueConnection:唯一。和直连相同,但是只能一对一连接。

所以如果信号和槽在同一个线程里头,它的行为就相当于直接调用了槽函数。

多线程的情况下,qt引入了事件循环和线程归属。虽然一个对象的内存对所有的线程均是可见的,但是QT的元对象系统会记录对象所在的线程。每一个线程都有自己的事件循环。事件循环本身也是一个对象,它也有自己的信号与槽。

所以当出现跨线程的情况,connect会调用全局的Addevent函数,根据receiver的所属线程参数找到该线程的事件循环队列,注册一个事件。之后再由事件循环去调用槽函数。

阻塞队列的情况是类似的,在注册事件的时候传入了一个信号量作为参数,注册完之后本线程就阻塞,直到槽函数执行完毕释放信号量了,才解除阻塞。


当其他人都认为你要鸽的时候,你鸽了,亦是一种不鸽