GCD?
Serial vs. Concurrent 串行 vs. 并發(fā)
這些術(shù)語(yǔ)描述當(dāng)任務(wù)相對(duì)于其它任務(wù)被執(zhí)行,任務(wù)串行執(zhí)行就是每次只有一個(gè)任務(wù)被執(zhí)行铃肯,任務(wù)并發(fā)執(zhí)行就是在同一時(shí)間可以有多個(gè)任務(wù)被
執(zhí)行患亿。
在 GCD 中,這些術(shù)語(yǔ)描述當(dāng)一個(gè)函數(shù)相對(duì)于另一個(gè)任務(wù)完成押逼,此任務(wù)是該函數(shù)要求 GCD 執(zhí)行的步藕。一個(gè)_同步_函數(shù)只在完成了它預(yù)定的任務(wù)后才返回。
一個(gè)_異步_函數(shù)挑格,剛好相反咙冗,會(huì)立即返回,預(yù)定的任務(wù)會(huì)完成但不會(huì)等它完成漂彤。因此雾消,一個(gè)異步函數(shù)不會(huì)阻塞當(dāng)前線程去執(zhí)行下一個(gè)函數(shù)
Deadlock 死鎖
兩個(gè)(有時(shí)更多)東西——在大多數(shù)情況下灾搏,是線程——所謂的死鎖是指它們都卡住了,并等待對(duì)方完成或執(zhí)行其它操作立润。第一個(gè)不能完成是因?yàn)樗诘却诙€(gè)的完成狂窑。但第二個(gè)也不能完成,因?yàn)樗诘却谝粋€(gè)的完成桑腮。
Context Switch 上下文切換
一個(gè)上下文切換指當(dāng)你在單個(gè)進(jìn)程里切換執(zhí)行不同的線程時(shí)存儲(chǔ)與恢復(fù)執(zhí)行狀態(tài)的過(guò)程泉哈。這個(gè)過(guò)程在編寫多任務(wù)應(yīng)用時(shí)很普遍,但會(huì)帶來(lái)一些額外的開(kāi)銷
Concurrency vs Parallelism 并發(fā)與并行
并發(fā)和并行通常被一起提到破讨,所以值得花些時(shí)間解釋它們之間的區(qū)別丛晦。
并發(fā)代碼的不同部分可以“同步”執(zhí)行。然而添忘,該怎樣發(fā)生或是否發(fā)生都取決于系統(tǒng)采呐。多核設(shè)備通過(guò)并行來(lái)同時(shí)執(zhí)行多個(gè)線程;然而搁骑,為了使單核設(shè)備也能實(shí)現(xiàn)這一點(diǎn)斧吐,它們必須先運(yùn)行一個(gè)線程,執(zhí)行一個(gè)上下文切換仲器,然后運(yùn)行另一個(gè)線程或進(jìn)程煤率。這通常發(fā)生地足夠快以致給我們并發(fā)執(zhí)行地錯(cuò)覺(jué)
Queue Types 隊(duì)列類型
首先,系統(tǒng)提供給你一個(gè)叫做?主隊(duì)列(main queue)?的特殊隊(duì)列乏冀。和其它串行隊(duì)列一樣蝶糯,這個(gè)隊(duì)列中的任務(wù)一次只能執(zhí)行一個(gè)。然而辆沦,它能保證所有的任務(wù)都在主線程執(zhí)行昼捍,而主線程是唯一可用于更新 UI 的線程。這個(gè)隊(duì)列就是用于發(fā)生消息給?UIView?或發(fā)送通知的肢扯。
系統(tǒng)同時(shí)提供給你好幾個(gè)并發(fā)隊(duì)列妒茬。它們叫做?全局調(diào)度隊(duì)列(Global Dispatch Queues)?。目前的四個(gè)全局隊(duì)列有著不同的優(yōu)先級(jí):background蔚晨、low乍钻、default?以及?high。要知道铭腕,Apple 的 API 也會(huì)使用這些隊(duì)列银择,所以你添加的任何任務(wù)都不會(huì)是這些隊(duì)列中唯一的任務(wù)。
最后累舷,你也可以創(chuàng)建自己的串行隊(duì)列或并發(fā)隊(duì)列浩考。這就是說(shuō),至少有_五個(gè)_隊(duì)列任你處置:主隊(duì)列被盈、四個(gè)全局調(diào)度隊(duì)列怀挠,再加上任何你自己創(chuàng)建的隊(duì)列析蝴。
以上是調(diào)度隊(duì)列的大框架!
GCD 的“藝術(shù)”歸結(jié)為選擇合適的隊(duì)列來(lái)調(diào)度函數(shù)以提交你的工作绿淋。體驗(yàn)這一點(diǎn)的最好方式是走一遍下邊的列子,我們沿途會(huì)提供一些一般性的建議尝盼。
線程安全的代碼能在多線程或并發(fā)任務(wù)中被安全的調(diào)用吞滞,而不會(huì)導(dǎo)致任何問(wèn)題(數(shù)據(jù)損壞,崩潰盾沫,等)裁赠。線程不安全的代碼在某個(gè)時(shí)刻只能在一個(gè)上下文中運(yùn)行。一個(gè)線程安全代碼的例子是?NSDictionary?赴精。你可以在同一時(shí)間在多個(gè)線程中使用它而不會(huì)有問(wèn)題佩捞。另一方面,NSMutableDictionary?就不是線程安全的蕾哟,應(yīng)該保證一次只能有一個(gè)線程訪問(wèn)它一忱。
?dispatch_sync?:
自定義串行隊(duì)列:在這個(gè)狀況下要非常小心!如果你正運(yùn)行在一個(gè)隊(duì)列并調(diào)用?dispatch_sync?放在同一個(gè)隊(duì)列谭确,那你就百分百地創(chuàng)建了一個(gè)死鎖帘营。
主隊(duì)列(串行):同上面的理由一樣,必須非常小心逐哈!這個(gè)狀況同樣有潛在的導(dǎo)致死鎖的情況芬迄。
并發(fā)隊(duì)列:這才是做同步工作的好選擇,不論是通過(guò)調(diào)度障礙昂秃,或者需要等待一個(gè)任務(wù)完成才能執(zhí)行進(jìn)一步處理的情況禀梳。
?dispatch_async :
自定義串行隊(duì)列:當(dāng)你想串行執(zhí)行后臺(tái)任務(wù)并追蹤它時(shí)就是一個(gè)好選擇。這消除了資源爭(zhēng)用肠骆,因?yàn)槟阒酪淮沃挥幸粋€(gè)任務(wù)在執(zhí)行算途。注意若你需要來(lái)自某個(gè)方法的數(shù)據(jù),你必須內(nèi)聯(lián)另一個(gè) Block 來(lái)找回它或考慮使用?dispatch_sync哗戈。
主隊(duì)列(串行):這是在一個(gè)并發(fā)隊(duì)列上完成任務(wù)后更新 UI 的共同選擇郊艘。要這樣做,你將在一個(gè) Block 內(nèi)部編寫另一個(gè) Block 唯咬。以及纱注,如果你在主隊(duì)列調(diào)用?dispatch_async?到主隊(duì)列,你能確保這個(gè)新任務(wù)將在當(dāng)前方法完成后的某個(gè)時(shí)間執(zhí)行胆胰。
并發(fā)隊(duì)列:這是在后臺(tái)執(zhí)行非 UI 工作的共同選擇狞贱。
dispatch_after?
自定義串行隊(duì)列:在一個(gè)自定義串行隊(duì)列上使用?dispatch_after?要小心。你最好堅(jiān)持使用主隊(duì)列蜀涨。
主隊(duì)列(串行):是使用?dispatch_after?的好選擇瞎嬉;Xcode 提供了一個(gè)不錯(cuò)的自動(dòng)完成模版蝎毡。
并發(fā)隊(duì)列:在并發(fā)隊(duì)列上使用?dispatch_after?也要小心;你會(huì)這樣做就比較罕見(jiàn)氧枣。還是在主隊(duì)列做這些操作吧沐兵。
dispatch_once
以線程安全的方式執(zhí)行且僅執(zhí)行其代碼塊一次。試圖訪問(wèn)臨界區(qū)(即傳遞給?dispatch_once?的代碼)的不同的線程會(huì)在臨界區(qū)已有一個(gè)線程的情況下被阻塞便监,直到臨界區(qū)完成為止
需要記住的是扎谎,這只是讓訪問(wèn)共享實(shí)例線程安全。它絕對(duì)沒(méi)有讓類本身線程安全烧董。類中可能還有其它競(jìng)態(tài)條件毁靶,例如任何操縱內(nèi)部數(shù)據(jù)的情況。這些需要用其它方式來(lái)保證線程安全逊移,例如同步訪問(wèn)數(shù)據(jù)
Dispatch barriers
自定義串行隊(duì)列:一個(gè)很壞的選擇预吆;障礙不會(huì)有任何幫助,因?yàn)椴还茉鯓痈烊粋€(gè)串行隊(duì)列一次都只執(zhí)行一個(gè)操作拐叉。
全局并發(fā)隊(duì)列:要小心;這可能不是最好的主意胶背,因?yàn)槠渌到y(tǒng)可能在使用隊(duì)列而且你不能壟斷它們只為你自己的目的巷嚣。
自定義并發(fā)隊(duì)列:這對(duì)于原子或臨界區(qū)代碼來(lái)說(shuō)是極佳的選擇。任何你在設(shè)置或?qū)嵗男枰€程安全的事物都是使用障礙的最佳候選钳吟。
Dispatch Groups(調(diào)度組)
Dispatch Group 會(huì)在整個(gè)組的任務(wù)都完成時(shí)通知你廷粒。這些任務(wù)可以是同步的,也可以是異步的红且,即便在不同的隊(duì)列也行坝茎。而且在整個(gè)組的任務(wù)都完成時(shí),Dispatch Group 可以用同步的或者異步的方式通知你暇番。因?yàn)橐O(jiān)控的任務(wù)在不同隊(duì)列嗤放,那就用一個(gè)?dispatch_group_t?的實(shí)例來(lái)記下這些不同的任務(wù)。
當(dāng)組中所有的事件都完成時(shí)壁酬,GCD 的 API 提供了兩種通知方式次酌。
第一種是?dispatch_group_wait?,它會(huì)阻塞當(dāng)前線程舆乔,直到組里面所有的任務(wù)都完成或者等到某個(gè)超時(shí)發(fā)生岳服。
dispatch_group_enter?手動(dòng)通知 Dispatch Group 任務(wù)已經(jīng)開(kāi)始。你必須保證?dispatch_group_enter?和?dispatch_group_leave?成對(duì)出現(xiàn)希俩,否則你可能會(huì)遇到詭異的崩潰問(wèn)題
Dispatch Group
自定義串行隊(duì)列:它很適合當(dāng)一組任務(wù)完成時(shí)發(fā)出通知吊宋。
主隊(duì)列(串行):它也很適合這樣的情況。但如果你要同步地等待所有工作地完成颜武,那你就不應(yīng)該使用它璃搜,因?yàn)槟悴荒茏枞骶€程拖吼。然而,異步模型是一個(gè)很有吸引力的能用于在幾個(gè)較長(zhǎng)任務(wù)(例如網(wǎng)絡(luò)調(diào)用)完成后更新 UI 的方式这吻。
并發(fā)隊(duì)列:它也很適合 Dispatch Group 和完成時(shí)通知
dispatch_apply
自定義串行隊(duì)列:串行隊(duì)列會(huì)完全抵消?dispatch_apply?的功能吊档;你還不如直接使用普通的?for?循環(huán)。
主隊(duì)列(串行):與上面一樣橘原,在串行隊(duì)列上不適合使用?dispatch_apply?籍铁。還是用普通的?for?循環(huán)吧。
并發(fā)隊(duì)列:對(duì)于并發(fā)循環(huán)來(lái)說(shuō)是很好選擇趾断,特別是當(dāng)你需要追蹤任務(wù)的進(jìn)度時(shí)。