Runloop
-
什么是Runloop
- Runloop就是消息循環(huán),每一個(gè)線程內(nèi)部都有一個(gè)消息循壞藕帜。
- 只有主線程的消息循環(huán)默認(rèn)開啟叉信,子線程的消息循環(huán)默認(rèn)不開啟栓始。
-
消息循環(huán)的目的
- 保證程序不退出胸完。
- 負(fù)責(zé)處理輸入事件书释。Runloop接收輸入事件來自兩種不同的來源:輸入源(input source)和定時(shí)源(timer source)。
- 如果沒有事件發(fā)生赊窥,會(huì)讓程序進(jìn)入休眠狀態(tài)爆惧。
-
使用消息循環(huán)的時(shí)候必須指定兩件事情
- 輸入事件:輸入源和定時(shí)源。
- 消息循環(huán)模式锨能,此模式必須跟當(dāng)前消息循環(huán)使用的模式匹配检激。
-
消息循環(huán)模式
- NSDefaultRunLoopMode。
- NDRunLoopCommonModes腹侣。
-
子線程的Runloop
- 啟動(dòng)子線程的消息循環(huán)
啟動(dòng)子線程的消息循環(huán)
GCD
-
什么是GCD
- 全稱是Grand Center Dispatch(CPU調(diào)度中心,調(diào)度的是任務(wù)和線程齿穗,把任務(wù)交給線程執(zhí)行)傲隶。
- 純C語言,提供了非常多窃页、強(qiáng)大的函數(shù)跺株。
- GCD是 libdispatch的統(tǒng)稱,而libdispatch是蘋果為了在多核硬件(如iOS脖卖、OSX)上提供處理并發(fā)代碼的庫乒省。
-
GCD的優(yōu)勢(shì)
- GCD是蘋果公司為多核的并行運(yùn)算提出的解決方案。
- GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核畦木。
- GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程袖扛、調(diào)度任務(wù)、銷毀線程)十籍。
- 只要告訴GCD想要執(zhí)行什么任務(wù)蛆封,不需要編寫任何線程管理的代碼。
- GCD能幫助你分配繁重計(jì)算任務(wù)并且保持在后臺(tái)運(yùn)行勾栗,幫你提高App的響應(yīng)程度惨篱。
- GCD幫你提供易用的并發(fā)模式,不僅僅是鎖和線程围俘,幫助你避免在開發(fā)并發(fā)程序時(shí)的bug砸讳。
- GCD具有在常見模式(如單例模式)以最原始的語句優(yōu)化,是你的代碼具有更高的效率界牡。
-
GCD術(shù)語
- 串行簿寂、并行:所謂串行并行描述是相對(duì)而言的,串行是指在同一時(shí)間只執(zhí)行一個(gè)任務(wù)欢揖,并行是指在同一時(shí)間可能執(zhí)行多個(gè)任務(wù)陶耍。
- 同步、異步:在GCD中她混,同步異步是為了描述一個(gè)函數(shù)相對(duì)于該函數(shù)要求GCD執(zhí)行完成的另一個(gè)任務(wù)烈钞。同步方法只在它完成它需要做的任務(wù)后才會(huì)返回泊碑。異步方法剛好和同步方法相反,它不會(huì)等待任務(wù)完成才返回毯欣,它會(huì)立即返回馒过。所以異步不會(huì)阻塞當(dāng)前線程執(zhí)行另一個(gè)任務(wù)(方法\函數(shù))。
- 死鎖:兩個(gè)(有時(shí)更多)東西——在大多數(shù)情況下酗钞,是線程——所謂的死鎖是指它們都卡住了腹忽,并等待對(duì)方完成或執(zhí)行其它操作。第一個(gè)不能完成是因?yàn)樗诘却诙€(gè)的完成砚作。但第二個(gè)也不能完成窘奏,因?yàn)樗诘却谝粋€(gè)的完成。
- 線程安全:線程安全的代碼在多線程或并發(fā)任務(wù)中被安全的調(diào)用葫录,而不會(huì)導(dǎo)致任何問題(數(shù)據(jù)損壞着裹、崩潰等)。線程不安全的代碼在某個(gè)時(shí)刻只能在一個(gè)上下文中運(yùn)行米同。一個(gè)線程安全代碼的例子是NSDictionary骇扇,你可以在同一時(shí)間在多個(gè)線程中使用它而不會(huì)有問題,另一方面面粮,NSMutableDictionary就不是線程安全的少孝,應(yīng)該保證一次只能一個(gè)線程訪問它。
-
GCD使用步驟
- 定制任務(wù):確定要執(zhí)行的操作(方法)熬苍。
- 將任務(wù)添加到隊(duì)列中稍走,并按照指定的同步或異步方式執(zhí)行任務(wù)。
- GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出冷溃,放到對(duì)應(yīng)的線程中執(zhí)行钱磅。
- 任務(wù)的取出遵循隊(duì)列的先進(jìn)先出原則。
-
隊(duì)列
- GCD提供了dispatch queues(調(diào)度隊(duì)列)來執(zhí)行代碼段似枕,這些隊(duì)列以FIFO(先進(jìn)先出)的方式來管理你用GCD提交的任務(wù)盖淡。這保證了你先提交的任務(wù)先執(zhí)行,即第一個(gè)任務(wù)添加到隊(duì)列中就第一個(gè)開始執(zhí)行凿歼,第二個(gè)添加的任務(wù)將第二個(gè)執(zhí)行褪迟,直到隊(duì)列的最后一個(gè)任務(wù)。
- 所有的調(diào)度隊(duì)列(dispatch queues)自身都是線程安全的答憔,你能通過多個(gè)現(xiàn)成來訪問它們味赃。
- 串行隊(duì)列:在串行隊(duì)列中,同一個(gè)隊(duì)列中只有一個(gè)任務(wù)在執(zhí)行虐拓,每個(gè)任務(wù)只有在前一個(gè)任務(wù)執(zhí)行完成后才能開始執(zhí)行心俗。而且,你不知道在一個(gè)Block(任務(wù))執(zhí)行結(jié)束到下一個(gè)Block(任務(wù))開始執(zhí)行之間的這段時(shí)間是多長。這些任務(wù)的執(zhí)行時(shí)機(jī)完全是在GCD的控制之下城榛,你能唯一保證的是:GCD一次只能執(zhí)行一個(gè)任務(wù)以及它會(huì)按照你添加到隊(duì)列中的先后順序來執(zhí)行任務(wù)揪利。在串行隊(duì)列中,不會(huì)有兩個(gè)任務(wù)并發(fā)執(zhí)行狠持。
- 并發(fā)隊(duì)列:在并發(fā)隊(duì)列中疟位,你唯一能保證的是,這些任務(wù)會(huì)按照被添加的順序開始執(zhí)行喘垂,但是任務(wù)可以以任何順序完成甜刻,你不知道在執(zhí)行下一個(gè)任務(wù)是從什么時(shí)候開始,或者說任意時(shí)刻有多個(gè)Block(任務(wù))執(zhí)行正勒,這個(gè)完全是取決于GCD得院。
- 在什么時(shí)候執(zhí)行Block完全是由GCD來決定,如果一個(gè)Block的執(zhí)行時(shí)間與另外一個(gè)Block重疊章贞,GCD就會(huì)決定是否將另一個(gè)任務(wù)運(yùn)行在不同的核上尿招,除非那個(gè)核不能使用,否則GCD將通過切換上下文方式將任務(wù)運(yùn)行在另一個(gè)核上阱驾。
-
GCD的核心概念
- GCD的核心概念:
- 任務(wù):要執(zhí)行的操作(方法)。
- 隊(duì)列:用來存放任務(wù)的集合怪蔑。
- 將任務(wù)添加到隊(duì)列里覆,指定任務(wù)執(zhí)行的方法。
- 任務(wù):使用block封裝缆瓣,block就是一個(gè)提前準(zhǔn)備好的代碼塊喧枷,在需要的時(shí)候執(zhí)行。
-
隊(duì)列:
- 串行隊(duì)列:一個(gè)接一個(gè)的調(diào)度任務(wù)弓坞。
- 并發(fā)隊(duì)列:可以同時(shí)調(diào)度多個(gè)任務(wù)隧甚。
- 主隊(duì)列:全局串行隊(duì)列吧寺,由主線程串行調(diào)度任務(wù)失暴,并且只有一個(gè)剃袍。
- 全局隊(duì)列:沒有名稱的并發(fā)隊(duì)列拣宏。
- 執(zhí)行任務(wù)的函數(shù):
- 同步執(zhí)行:當(dāng)前指令不完成碱茁,就不會(huì)執(zhí)行下一條指令捏境。
- 異步執(zhí)行:當(dāng)前指令不完成妓盲,同樣可以執(zhí)行下一條指令谷遂。
- GCD的核心概念:
隊(duì)列和執(zhí)行的幾種情況
- 串行隊(duì)列超歌,同步任務(wù)
- 不新開線程砍艾,順序(同步)執(zhí)行。
- 在當(dāng)前線程執(zhí)行巍举。
串行隊(duì)列脆荷,同步任務(wù)
- 串行隊(duì)列,異步任務(wù)
- 開一個(gè)線程,順序(同步)執(zhí)行蜓谋。
- 只有一個(gè)線程梦皮,因?yàn)槭谴嘘?duì)列,只有一個(gè)線程就可以按順序執(zhí)行隊(duì)列中的所有任務(wù)孤澎。
串行隊(duì)列届氢,異步任務(wù)
- 并發(fā)隊(duì)列,異步任務(wù)
- 開多個(gè)線程覆旭,異步執(zhí)行退子。
- 每次開啟多少個(gè)線程是不固定的(線程數(shù),不由我們控制)型将。
- 線程數(shù)由GCD來決定的寂祥。
并發(fā)隊(duì)列,異步任務(wù)
- 并發(fā)隊(duì)列七兜,同步任務(wù)
- 不新開線程丸凭,順序(同步)執(zhí)行。
- 和串行隊(duì)列同步執(zhí)行效果相同腕铸。
并發(fā)隊(duì)列惜犀,同步任務(wù)
- 主隊(duì)列,異步任務(wù)
- 不新開線程狠裹,同步執(zhí)行虽界。
- 主隊(duì)列特點(diǎn):如果主線程正在執(zhí)行代碼暫時(shí)不調(diào)度任務(wù),等主線程執(zhí)行結(jié)束后再執(zhí)行任務(wù)涛菠。
- 主隊(duì)列又叫全局串行隊(duì)列莉御,主隊(duì)列的任務(wù)都由主線程來調(diào)度,不會(huì)開啟新線程俗冻。
主隊(duì)列礁叔,異步任務(wù)
- 主隊(duì)列,同步任務(wù)
- 程序執(zhí)行不出來迄薄,造成“死鎖”琅关。
- 死鎖的原因,當(dāng)程序執(zhí)行到這段代碼的時(shí)候:
- 主隊(duì)列:如果主線程正在執(zhí)行代碼讥蔽,就不調(diào)度任務(wù)死姚。
- 同步任務(wù):如果第一個(gè)任務(wù)沒有執(zhí)行,就繼續(xù)等待第一個(gè)任務(wù)執(zhí)行完成勤篮,再執(zhí)行下一個(gè)任務(wù)都毒。
- 此時(shí)互相等待,程序無法往下執(zhí)行(死鎖)碰缔。
- 主隊(duì)列和串行隊(duì)列的區(qū)別:
- 串行隊(duì)列:必須等待一個(gè)任務(wù)執(zhí)行完成账劲,再調(diào)度另一個(gè)任務(wù)。
- 主隊(duì)列:以先進(jìn)先出調(diào)度任務(wù),如果主線程上有代碼在執(zhí)行瀑焦,主隊(duì)列不會(huì)調(diào)度任務(wù)腌且。
主隊(duì)列,同步任務(wù)
- 解決死鎖
- 將(主隊(duì)列榛瓮,同步任務(wù))放入非主隊(duì)列異步任務(wù)中铺董,可以解決死鎖。
- (主隊(duì)列禀晓,同步任務(wù))在子線程中運(yùn)行精续,同步執(zhí)行不用等待主線程執(zhí)行此同步執(zhí)行的任務(wù)。
解決死鎖
- 總結(jié)
同步\異步 | 全局并行隊(duì)列 | 手動(dòng)創(chuàng)建串行隊(duì)列 | 主隊(duì)列 |
---|---|---|---|
同步(sync) | 沒有開啟新線程 串行執(zhí)行任務(wù) | 沒有開啟新線程 串行執(zhí)行任務(wù) | 會(huì)死鎖 |
異步(async) | 有開啟新線程 并行執(zhí)行任務(wù) | 有開啟新線程 并行執(zhí)行任務(wù) | 沒有開啟新線程 串行執(zhí)行任務(wù) |
同步任務(wù)
-
同步任務(wù)的作用
- 在網(wǎng)絡(luò)開發(fā)中粹懒,通常會(huì)把很多任務(wù)放在后臺(tái)異步執(zhí)行重付,有些任務(wù)會(huì)彼此“依賴”,應(yīng)該使用同步任務(wù)處理凫乖。
-
同步任務(wù)的特點(diǎn)
- 隊(duì)列調(diào)度多個(gè)異步任務(wù)前确垫,指定一個(gè)或者多個(gè)同步任務(wù),讓所有的異步任務(wù)都等待同步任務(wù)完成帽芽,這就是所謂的“依賴”關(guān)系删掀。
演示后臺(tái)有依賴關(guān)系的耗時(shí)操作
全局隊(duì)列
- 全局隊(duì)列
- 全局隊(duì)列的本質(zhì)就是并發(fā)隊(duì)列。
- 獲取全局隊(duì)列最簡(jiǎn)單方式
dispatch_get_global_queue(0,0);
导街。
// 獲取全局隊(duì)列的函數(shù)
dispatch_get_global_queue(long identifier, unsigned long flags);
// 第一個(gè)參數(shù) identifier
// 在iOS7中表示調(diào)度的優(yōu)先級(jí)(讓線程響應(yīng)的更快還是更慢)
DISPATCH_QUEUE_PRIORITY_HIGH 2 高優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認(rèn)優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后臺(tái)優(yōu)先級(jí)
// 在iOS8中表示服務(wù)質(zhì)量
QOS_CLASS_USER_INTERACTIVE 用戶希望線程快點(diǎn)執(zhí)行完畢 不要使用耗時(shí)操作
QOS_CLASS_USER_INITIATED 用戶需要的 不要使用耗時(shí)操作
QOS_CLASS_DEFAULT 默認(rèn)
QOS_CLASS_UTILITY 耗時(shí)操作
QOS_CLASS_BACKGROUND 后臺(tái)
QOS_CLASS_UNSPECIFIED 0 未指定優(yōu)先級(jí)
// 為了在iOS7和iOS8中適配此參數(shù) 可以直接傳入0
DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND
// 第二個(gè)參數(shù) 為將來保留使用 始終傳入0
- 全局隊(duì)列和并發(fā)隊(duì)列的區(qū)別
- 并發(fā)隊(duì)列有名稱爬迟,可以跟蹤錯(cuò)誤。全局隊(duì)列沒有名稱菊匿。
- 在ARC中不需要考慮釋放內(nèi)存,
dispatch_release(q);
不允許調(diào)用计福。 - 在MRC中需要手動(dòng)管理內(nèi)存跌捆,并發(fā)隊(duì)列是create創(chuàng)建出來的,在MRC中見到create就要release象颖,全局隊(duì)列則不需要release佩厚,全局隊(duì)列只有一個(gè)。
- 一般我們使用全局隊(duì)列说订。
延時(shí)操作
- 延時(shí)操作
// dispatch_after函數(shù)的定義
dispatch_after(dispatch_time_t when,
dispatch_queue_t queue,
dispatch_block_t block);
// dispatch_after的參數(shù)
// 參數(shù)1 dispatch_time_t when 多少納秒之后執(zhí)行
// 參數(shù)2 dispatch_queue_t queue 任務(wù)添加到哪個(gè)隊(duì)列
// 參數(shù)3 dispatch_block_t block 要執(zhí)行的任務(wù)
指定串行隊(duì)列延時(shí)操作
一次性執(zhí)行
- 程序中有的時(shí)候希望一個(gè)方法只被執(zhí)行一次
一個(gè)方法只被執(zhí)行一次
- 讓dispatch_once執(zhí)行在子線程上
讓dispatch_once執(zhí)行在子線程上
- dispatch_once是線程安全的
- 多個(gè)線程的時(shí)候也是執(zhí)行一次抄瓦。
dispatch_once是線程安全的
調(diào)度組Dispatch Group
-
Dispatch Group調(diào)度組
- Dispatch Group會(huì)在被添加進(jìn)調(diào)度組的所有任務(wù)都完成時(shí)通知你,這些任務(wù)可以是同步的陶冷,也可以是異步的钙姊,即便在不同的隊(duì)列也行。
- Dispatch Group調(diào)度組只有異步執(zhí)行埂伦。
- 創(chuàng)建一個(gè)新的Dispatch Group煞额,它的作用就像一個(gè)用于未完成任務(wù)的計(jì)數(shù)器。
-
dispatch_group_enter
手動(dòng)通知Dispatch Group任務(wù)已經(jīng)開始,必須保證dispatch_group_enter
和dispatch_group_leave
成對(duì)出現(xiàn)膊毁,相當(dāng)于確保進(jìn)入Group的次數(shù)和離開Group的次數(shù)相等胀莹,否則可能會(huì)遇到詭異的崩潰問題。 - 包含不同隊(duì)列類型的調(diào)度組Dispatch Group
- 自定義串行隊(duì)列:它很適合當(dāng)一組任務(wù)完成時(shí)發(fā)出通知婚温。
- 主隊(duì)列(串行):它也很適合這種情況描焰。但如果你要同步地等待所有工作地完成,就不應(yīng)該使用栅螟,因?yàn)椴荒茏枞骶€程荆秦,會(huì)導(dǎo)致死鎖。然而嵌巷,主隊(duì)列的異步任務(wù)能用于在幾個(gè)較長任務(wù)完成后更新UI的方式萄凤。
- 并發(fā)隊(duì)列:它也很適合Dispatch Group和完成時(shí)通知。
-
dispatch_group_wait
- 會(huì)阻塞當(dāng)前線程搪哪,直到組里面所有的任務(wù)都完成或者等到某個(gè)超時(shí)完成靡努。
- 如果在所有任務(wù)完成前超時(shí)了,該函數(shù)會(huì)返回一個(gè)非零值晓折』箅可以對(duì)此返回值做條件判斷以確定是否超出等待周期。
DISPATCH_TIME_FOREVER
讓這個(gè)組永遠(yuǎn)等待漓概。
-
dispatch_apply
- dispatch_apply就像一個(gè)for循環(huán)漾月,但它能并發(fā)地執(zhí)行不同的迭代。
- 這個(gè)函數(shù)是同步執(zhí)行的胃珍,所以和普通的for循環(huán)一樣梁肿,它只會(huì)在所有工作完成后才返回。
- 對(duì)于并發(fā)循環(huán)使用dispatch_apply觅彰,可以幫助你追蹤任務(wù)的進(jìn)度吩蔑。
-
dispatch_group_notify
- 以異步的方式工作。
- 不會(huì)阻塞任何線程填抬。
- 有時(shí)候需要在多個(gè)異步任務(wù)都執(zhí)行完成之后繼續(xù)做某些事情烛芬。
下載歌曲,等所有的歌曲都下載完畢之后轉(zhuǎn)到主線程提示用戶