之前的 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.pngrunloop通过Core Foundation添加timer
image.pngrunloop启动之后有超时,图中设置了10秒超时
虽然timer每5秒触发一次,但timer却是基于runloop调度的
过10秒之后 runloop超时 退出,因为采用了while,所以runloop退出之后又重新启动
添加timer - 直观演示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属于其他线程, 就会崩溃
网友评论