之前的 Runloop原理(一) 中測(cè)試是在主線程進(jìn)行的,接下來(lái)在子線程中進(jìn)行
以下涉及的內(nèi)容均是針對(duì)子線程的
Starting the Run Loop
只有在子線程中啟動(dòng)runloop 才是必要的
runloop必須至少有一個(gè)inpurt source 或者 timer,如果一個(gè)也沒(méi)有,則runloop 馬上退出
啟動(dòng)runloop有幾種方式
-
Unconditionally
無(wú)條件進(jìn)入runloop是最簡(jiǎn)單的選擇,但也是最不可取的;
無(wú)條件地運(yùn)行runloop會(huì)使線程進(jìn)入永久循環(huán),這使得你對(duì)runloop本身幾乎沒(méi)有控制權(quán)
你可以添加和刪除input sources和timer,但唯一能停止runloop的方法是終止它,也沒(méi)有辦法在custom mode中運(yùn)行runloop
-
With a set time limit
設(shè)定限制時(shí)間
如果設(shè)定超時(shí),runloop將一直運(yùn)行直到事件到達(dá)或者分配的時(shí)間過(guò)期為止
-
In a particular mode
在特定mode下
線程主入口框架版本

子線程中 沒(méi)有啟動(dòng)runloop,timer是沒(méi)辦法調(diào)度執(zhí)行的,而且子線程一啟動(dòng),馬上就銷毀了

無(wú)條件啟動(dòng)runloop,timer調(diào)度執(zhí)行, 而且直接run runloop,后面的代碼執(zhí)行阻塞
此時(shí) 主動(dòng)讓線程exit, timer也停止,說(shuō)明timer調(diào)度是依托于runloop的
runloop添加非基于port的input source

對(duì)于非基于port的input source,runloop休眠,監(jiān)視source的signal事件,如果沒(méi)有其他線程對(duì)source的signal,runloop將超時(shí) 退出
也就是

runloop通過(guò)Core Foundation添加timer

runloop啟動(dòng)之后有超時(shí),圖中設(shè)置了10秒超時(shí)
雖然timer每5秒觸發(fā)一次,但timer卻是基于runloop調(diào)度的
過(guò)10秒之后 runloop超時(shí) 退出,因?yàn)椴捎昧藈hile,所以runloop退出之后又重新啟動(dòng)
添加timer - 直觀演示runloop流程序列
為了更直觀的了解timer究竟是如何調(diào)度的,這里再總結(jié)一次
- 首先開(kāi)啟一個(gè)子線程
image.png
- runloop 注冊(cè)observer
image.png
- observer通知回調(diào)
image.png
- runloop 添加timer
image.png
- timer 任務(wù)
image.png
- 添加source (非基于port)
image.png
- source注冊(cè)回調(diào)函數(shù)
image.png
- 控制臺(tái)結(jié)果打印 -------
image.png
關(guān)于timer的流程打印總結(jié)說(shuō)明
首先進(jìn)入runloop (配置的啟動(dòng)方式為非阻塞的,超時(shí)會(huì)退出)
-
發(fā)現(xiàn)有timer,通知observer即將處理timer任務(wù)調(diào)度
但是timer的任務(wù)調(diào)度是按照計(jì)劃設(shè)置的時(shí)間表來(lái)執(zhí)行的,進(jìn)入runloop第一件事是先檢查是否配置了timer計(jì)劃
有計(jì)劃不代表runloop馬上要調(diào)度timer任務(wù)去執(zhí)行,而是看當(dāng)前的時(shí)間是否到了計(jì)劃的執(zhí)行時(shí)間,如果時(shí)間到了,就調(diào)度task執(zhí)行,如果沒(méi)到,心里有數(shù),繼續(xù)下一項(xiàng)議題 source
-
發(fā)現(xiàn)有source,通知observer即將處理source
source的執(zhí)行需要有signal,沒(méi)有signal什么也不做
跟timer類似,就是runloop調(diào)度計(jì)劃表里有source,但是調(diào)度執(zhí)行需要一個(gè)契機(jī), 這個(gè)契機(jī)就是signal
接下來(lái)通知observer即將休眠
-
當(dāng)前時(shí)間如果符合 timer計(jì)劃表里的時(shí)間點(diǎn),喚醒runloop, runloop 通知observer喚醒
喚醒發(fā)生在timer調(diào)度任務(wù)執(zhí)行之前
執(zhí)行timer任務(wù)
-
如果此時(shí)runloop 10秒超時(shí)未過(guò)期,將重新啟動(dòng)循環(huán), 即將進(jìn)入timer開(kāi)始
- 繼續(xù)一輪循環(huán) 即將進(jìn)入timer -> 即將處理source -> 休眠 -> timer喚醒 -> 馬上退出
如果此時(shí)runloop 10秒超時(shí)過(guò)期,runloop退出
source的執(zhí)行是怎么樣的
signal + wakeup
image.png
touch 一下
image.png
touch之后,runloop喚醒,此時(shí)你會(huì)發(fā)現(xiàn),即將進(jìn)入source之后 --> 執(zhí)行source --> 退出 --> 進(jìn)入runloop
喚醒runloop,循環(huán)輪詢一次,source執(zhí)行之后,會(huì)馬上退出。之所以這樣,是因?yàn)閠ouch顯式喚醒runloop會(huì)重新啟動(dòng)runloop
退出runloop
有兩種方式退出tunloop
runloop設(shè)置超時(shí)
告訴runloop stop --- CFRunLoopStop
雖然移除 input sources 和 timers可以引起runloop退出,但這是不可靠的. 一些系統(tǒng)例程將input sources添加到runloop中以處理所需的事件, 你的代碼可能不知道這些input sources,所以無(wú)法刪除它們,這將阻止runloop退出
線程安全和runloop對(duì)象
線程安全取決于你使用哪種api操作runloop
Core Foundation里的方法通常是線程安全的,可以在任何線程里調(diào)用
-
Cocoa NSRunLoop類不像Core Foundation對(duì)應(yīng)類那樣天生安全
如果你使用NSRunLoop修改runloop,你只能在此runloop的線程里操作
在線程里往runloop里添加input source 和timer,但是runloop屬于其他線程, 就會(huì)崩潰









