浅谈QT的信号发射与多线程模型

内容分享5天前发布
0 0 0

QT的对象和多线程模型设计得很复杂,但是又超级具有独创性,理念超级先进,我们在工作中,如果不能理解其内部工作原理的话,生搬硬套使用它,难免会踩坑。这里我将自己的理解分享给大家,希望能为处于困惑的朋友带来一点协助,如果我讲错了,欢迎留言一起探讨。

QT的普通对象

QT的对象结构超级复杂,这里我们重点关注两个结构:

  1. 依附的线程对象
    每个对象必须关联一个线程对象,它决定了该对象接收的信号由哪个线程处理。
    这个值是怎么初使化的呢?
    QT每个运行中的线程都有一个线程局部存储,它保存了当前线程关联的线程对象,关于线程对象的结构稍后再讲。在普通对象被创建时,在构造函数中会取出当前线程关联的线程对象,并赋值给它,当然这个值也是允许后面人工改变的。
  2. 信号槽映射表
    每个对象还保存有一张信号到槽函数的映射表,通俗地说,就是消息路由表,它定义了从本对象产生的信号应该触发的目标响应。信号只能从对象内部产生,当产生的信号被发射时,通过扫描这张表,找出对应信号关联的目标槽函数,并且查看目标对象依附的线程对象,如果与当前线程对象一样,则直接发起函数调用,否则需要将目标槽函数异步投递到它的依附线程对象中去处理。

QT的线程对象

线程对象派生自普通对象,它除了保存有依附的线程对象和信号槽映射表之外,还有两个独有的结构:

  1. 本线程对象的信号槽函数调用队列
    这是一个线程安全的队列,当源对象发射信号时,如果与目标对象依附的线程对象不一致,就将槽函数通过加锁的方式写到这里。
  2. 本线程对象的执行线程实体
    每个线程对象理论上都有一个执行实体,为什么这么说呢?
    这是由于在我们调用start()方法前,线程实体实则是没有运行的。运行中的线程实体会源源不断地从自己的信号槽函数调用队列中提取子项,然后在本线程中执行。

每个线程执行实体都有自己的线程对象,子线程对象很容易理解,由于它必须被明确的创建。那么主线程呢?
主线程比较特殊,它不像子线程对象先有对象再有执行实体,而是反过来先有执行对象(即main函数),所以QT的做法是自动为它初使化一个主线程对象,并与线程局部存储相关联。

从信号发射到槽函数被执行的过程

由于QT是一个源源不断的信号事件驱动系统,有些是内部事件,有些是用户自定义事件。所以我这里提取一个片断,并建立一个前提:

  1. 在主线程中创建源对象,在子线程中创建目标对象,此时源对象依附主线程,目标对象依附子线程。
  2. 为源对象初使化信号槽映射表,建立从源对象信号到目标对象槽函数的调用关系。

当源对象的信号被触发时,过程如下:

  1. 扫描源对象的信号槽映射表,取出该信号的所有映射项。
  2. 对每一个映射项,检查目标对象依附的线程对象,如果与当前线程对象一样,则直接调用目标槽函数。
  3. 否则,将目标槽函数异步投递到目标线程对象的调用队列中。
  4. 目标线程对象执行实体从队列中取出槽函数,执行立即调用。

最后补充一张图,方便理解:

浅谈QT的信号发射与多线程模型

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...