Runloop原理(二)

·  阅读 3526

Runloop原理(一)

之前的 Runloop原理(一) 中测试是在主线程进行的,接下来在子线程中进行

以下涉及的内容均是针对子线程的

Starting the Run Loop

只有在子线程中启动runloop 才是必要的

runloop必须至少有一个inpurt source 或者 timer,如果一个也没有,则runloop 马上退出

启动runloop有几种方式

  • Unconditionally

    无条件进入runloop是最简单的选择,但也是最不可取的;

    无条件地运行runloop会使线程进入永久循环,这使得你对runloop本身几乎没有控制权

    你可以添加和删除input sources和timer,但唯一能停止runloop的方法是终止它,也没有办法在custom mode中运行runloop

  • With a set time limit

    设定限制时间

    如果设定超时,runloop将一直运行直到事件到达或者分配的时间过期为止

  • In a particular mode

    在特定mode下

线程主入口框架版本

image.png

子线程中 没有启动runloop,timer是没办法调度执行的,而且子线程一启动,马上就销毁了

image.png

无条件启动runloop,timer调度执行, 而且直接run runloop,后面的代码执行阻塞

此时 主动让线程exit, timer也停止,说明timer调度是依托于runloop的

runloop添加非基于port的input source

image.png

对于非基于port的input source,runloop休眠,监视source的signal事件,如果没有其他线程对source的signal,runloop将超时 退出

也就是

image.png

将休眠, 阻塞,10秒内如果没有signal,runloop超时退出

由于 例子中采用 while循环,所以runloop每10秒如果超时了 就立即再次运行runloop

如果10秒内 别的线程对source signal了,则runloop马上退出,同样由于是while循环,立即运行runloop

别的线程对source signal之后,runloop 执行 perform

也就是说 runloop添加了input source,通过自己控制while循环来保障 runloop的持续运行

  • runloop 未等来source signal超时,runloop退出并立即启动

  • runloop 在超时之前 接收到 source signal,CFRunLoopRunInMode结果是kCFRunLoopRunHandledSource, 执行perform,退出,根据自己需要决定是否在此启动runloop

runloop通过Core Foundation添加timer

image.png

runloop启动之后有超时,图中设置了10秒超时

虽然timer每5秒触发一次,但timer却是基于runloop调度的

过10秒之后 runloop超时 退出,因为采用了while,所以runloop退出之后又重新启动

添加timer source - 直观演示runloop流程序列

为了更直观的了解timer究竟是如何调度的,这里再总结一次

  • 首先开启一个子线程

image.png

  • runloop 注册observer

image.png

  • observer通知回调

image.png

  • runloop 添加timer

image.png

  • timer任务

image.png

  • 添加source(非基于port)

image.png

  • source注册回调函数

image.png

  • 控制台结果打印 -------

image.png

关于timer的流程打印总结说明

  • 首先进入runloop (配置的启动方式为非阻塞的,超时会退出)

  • 发现有timer,通知observer即将处理timer任务调度

    但是timer的任务调度是按照计划设置的时间表来执行的,进入runloop第一件事是先检查是否配置了timer计划

    有计划不代表runloop马上要调度timer任务去执行,而是看当前的时间是否到了计划的执行时间,如果时间到了,就调度task执行,如果没到,心里有数,继续下一项议题 source

  • 发现有source,通知observer即将处理source

    source的执行需要有signal,没有signal什么也不做

    跟timer类似,就是runloop调度计划表里有source,但是调度执行需要一个契机, 这个契机就是signal

  • 接下来通知observer即将休眠

  • 当前时间如果符合 timer计划表里的时间点,唤醒runloop, runloop 通知observer唤醒

    唤醒发生在timer调度任务执行之前

  • 执行timer任务

  • 如果此时runloop 10秒超时未过期,将重新启动循环, 即将进入timer开始

    • 继续一轮循环 即将进入timer -> 即将处理source -> 休眠 -> timer唤醒 -> 马上退出
  • 如果此时runloop 10秒超时过期,runloop退出

source的执行是怎么样的

signal + wakeup

image.png

touch一下

image.png

touch之后,runloop唤醒,此时你会发现,即将进入source之后 --> 执行source --> 退出 --> 进入runloop

唤醒runloop,循环轮询一次,source执行之后,会马上退出。之所以这样,是因为touch显式唤醒runloop会重新启动runloop

退出runloop

有两种方式退出tunloop

  • runloop设置超时

  • 告诉runloop stop --- CFRunLoopStop

虽然移除 input sources 和 timers可以引起runloop退出,但这是不可靠的. 一些系统例程将input sources添加到runloop中以处理所需的事件, 你的代码可能不知道这些input sources,所以无法删除它们,这将阻止runloop退出

线程安全和runloop对象

线程安全取决于你使用哪种api操作runloop

  • Core Foundation里的方法通常是线程安全的,可以在任何线程里调用

  • Cocoa NSRunLoop类不像Core Foundation对应类那样天生安全

    如果你使用NSRunLoop修改runloop,你只能在此runloop的线程里操作

    在线程里往runloop里添加input source 和timer,但是runloop属于其他线程, 就会崩溃

分类:
iOS
标签:
分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改