GCD深入学习之GCD的初识
如果移動(dòng)端訪問不佳,可以訪問我的個(gè)人博客
現(xiàn)在網(wǎng)上關(guān)于GCD的介紹已經(jīng)很多了,在項(xiàng)目中也經(jīng)常用到,但是沒怎么深入研究過,打算寫一系列關(guān)于GCD使用,參考其他大神寫的博客和Apple的技術(shù)文檔總結(jié)一下,一是自己深入學(xué)習(xí)一下,二是以后忘了可以回過頭來溫習(xí)一下~
什么是GCD?
GCD全名是Grand Central Dispatch(大中央調(diào)度器),是系統(tǒng)級(jí)的,存在于libdispatch.dylib這個(gè)庫(kù)里,是Apple開發(fā)的一個(gè)多核編程的解決方法,它提供了一下幾種好處:
- GCD用純C編寫,可以提高應(yīng)用程序的響應(yīng)能力,更加高效;
- GCD使用簡(jiǎn)單,會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核),自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程),程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼,提供更容易并發(fā)模型,有助于避免并發(fā)錯(cuò)誤。
GCD的相關(guān)術(shù)語
要了解GCD,你必須熟悉相關(guān)的線程和并發(fā)的幾個(gè)概念。這些既可以是模糊的,微妙的,所以花點(diǎn)時(shí)間在GCD的背景下簡(jiǎn)要回顧一下他們。
串行和并發(fā)
在執(zhí)行任務(wù)時(shí),串行是任務(wù)順序執(zhí)行,執(zhí)行完一個(gè)下一個(gè)。并發(fā)就是任務(wù)可能同時(shí)執(zhí)行多個(gè)任務(wù)。在GCD中一個(gè)任務(wù)就是一個(gè)閉包,這比NSOperation中的任務(wù)更加容易理解。
同步和異步
在計(jì)算機(jī)領(lǐng)域,同步就是指一個(gè)進(jìn)程在執(zhí)行某個(gè)請(qǐng)求的時(shí)候,若該請(qǐng)求需要一段時(shí)間才能返回信息,那么這個(gè)進(jìn)程將會(huì)一直等待下去,直到收到返回信息才繼續(xù)執(zhí)行下去;異步是指進(jìn)程不需要一直等下去,而是繼續(xù)執(zhí)行下面的操作,不管其他進(jìn)程的狀態(tài)。當(dāng)有消息返回時(shí)系統(tǒng)會(huì)通知進(jìn)程進(jìn)行處理,這樣可以提高執(zhí)行的效率。
臨界區(qū)
通過對(duì)多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數(shù)據(jù)訪問。在任意時(shí)刻只允許一個(gè)線程對(duì)共享資源進(jìn)行訪問,如果有多個(gè)線程試圖訪問公共資 源,那么在有一個(gè)線程進(jìn)入后,其他試圖訪問公共資源的線程將被掛起,并一直等到進(jìn)入臨界區(qū)的線程離開,臨界區(qū)在被釋放后,其他線程才可以搶占。
死鎖
是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過程中,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程,簡(jiǎn)單來說就是A線程在等待B線程完成后執(zhí)行,B線程也在等待A線程完成后執(zhí)行,這樣就出現(xiàn)了死鎖的現(xiàn)象。
線程安全
線程安全的代碼可以從多個(gè)線程或并發(fā)任務(wù)安全地調(diào)用,而不會(huì)造成任何問題(數(shù)據(jù)損壞,系統(tǒng)崩潰等)。例如當(dāng)你多線程編程時(shí),你用let定義一個(gè)數(shù)組,因?yàn)樗侵蛔x的,你能在同一時(shí)間不同線程去使用它,而不會(huì)造成線程安全的問題,然而當(dāng)你用var定義一個(gè)數(shù)組時(shí)就不一樣了,它不是線程安全的,當(dāng)多個(gè)線程在同一時(shí)間訪問和修改數(shù)組時(shí)會(huì)產(chǎn)生不可預(yù)知的結(jié)果。
上下文切換
上下文切換是存儲(chǔ)和恢復(fù)執(zhí)行的狀態(tài),是你在一個(gè)進(jìn)程中執(zhí)行不同的線程之間切換的過程。這個(gè)過程是編寫多任務(wù)處理應(yīng)用程序時(shí)很常見,但也帶來了一些額外的開銷成本。就像并發(fā)就是通過切換上下文來實(shí)現(xiàn)的。
并發(fā)和并行
在多核設(shè)備上執(zhí)行任務(wù)時(shí),每個(gè)CPU可以單獨(dú)工作,每個(gè)CPU同時(shí)執(zhí)行不同的任務(wù),這就是并行,然而為了使單核的設(shè)備實(shí)現(xiàn)這種效果達(dá)到類似的效果,因?yàn)樗鼈冎挥幸粋€(gè)線程,它們只能通過快速的上下文切換來達(dá)到并行的假象,這就是并發(fā),如下圖所示:
GCD隊(duì)列
GCD用dispatch queues來處理提交的任務(wù),隊(duì)列用FIFO(先進(jìn)先出)的原理來管理這些任務(wù),所有的dispatch queues本身是線程安全的,你可以從多個(gè)線程去訪問它們,在GCD 中提供了兩種隊(duì)列,分別是串行隊(duì)列和并發(fā)隊(duì)列。
串行隊(duì)列
串行隊(duì)列保證同一時(shí)間隊(duì)列里只有一個(gè)任務(wù)在執(zhí)行,只有等待第一個(gè)任務(wù)執(zhí)行完成后才會(huì)執(zhí)行下一個(gè)任務(wù),你也不知道兩個(gè)任務(wù)之間的間隔時(shí)間是多少,如下圖所示:
使用串行隊(duì)列有一下的優(yōu)點(diǎn):
并發(fā)隊(duì)列
并發(fā)隊(duì)列可以讓你并行的執(zhí)行多個(gè)任務(wù)。任務(wù)按照它們被加入到隊(duì)列中的順序依次開始,但是它們都是并發(fā)的被執(zhí)行,并不需要彼此等待才開始。并發(fā)隊(duì)列能保證任務(wù)按同一順序開始,但你不能知道執(zhí)行的順序、執(zhí)行的時(shí)間以及在某一時(shí)刻正在被執(zhí)行任務(wù)的數(shù)量,具體如下圖所示:
GCD 的隊(duì)列類型
在使用過程中系統(tǒng)會(huì)自動(dòng)給每個(gè)應(yīng)用提供一個(gè)串行隊(duì)列和四個(gè)并發(fā)隊(duì)列,其中串行隊(duì)列為全局可用的串行隊(duì)列,在應(yīng)用的主線程中執(zhí)行任務(wù)且只有一個(gè),這個(gè)隊(duì)列被用來更新 App 的 UI,執(zhí)行所有與更新 UIViews 相關(guān)的任務(wù)。該隊(duì)列中同一時(shí)刻只執(zhí)行一個(gè)任務(wù),這就是為什么當(dāng)你在主隊(duì)列中運(yùn)行一個(gè)繁重的任務(wù)時(shí)UI會(huì)被阻塞的原因。GCD提供了一下三種隊(duì)列:
- 主隊(duì)列:任務(wù)以串行的方式執(zhí)行在您的應(yīng)用程序的主線程;
- 并發(fā)隊(duì)列:任務(wù)在先進(jìn)先出的順序列中移除,但并發(fā)運(yùn)行,可以按照任何順序完成;
- 串行隊(duì)列:以先進(jìn)先出順序執(zhí)行一次任務(wù)。
除主隊(duì)列之外,系統(tǒng)還提供了4個(gè)并發(fā)隊(duì)列。我們管它們叫 Global Dispatch queues(全局派發(fā)隊(duì)列)。這些隊(duì)列對(duì)整個(gè)應(yīng)用來說是全局可用的,彼此只有優(yōu)先級(jí)高低的區(qū)別。要使用其中一個(gè)全局并發(fā)隊(duì)列的話,你得使用 dispatch_get_global_queue 函數(shù)獲得一個(gè)你想要的隊(duì)列的引用,該函數(shù)的第一個(gè)參數(shù)取如下值:
DISPATCH_QUEUE_PRIORITY_HIGH:高優(yōu)先級(jí)的隊(duì)列,高于其他任務(wù)優(yōu)先級(jí)的隊(duì)列。
DISPATCH_QUEUE_PRIORITY_DEFAULT:默認(rèn)優(yōu)先級(jí)隊(duì)列,高于下面兩個(gè)優(yōu)先級(jí),
- DISPATCH_QUEUE_PRIORITY_LOW:低優(yōu)先級(jí)隊(duì)列,低于上面兩個(gè)優(yōu)先級(jí)。
- DISPATCH_QUEUE_PRIORITY_BACKGROUND:最低的優(yōu)先級(jí),使用它可以盡可能的減少對(duì)系統(tǒng)的影響。
你可以創(chuàng)建任何數(shù)量的串行或并發(fā)隊(duì)列。使用并發(fā)隊(duì)列的情況下,即使你可以自己創(chuàng)建,但是還是強(qiáng)烈建議你使用上面那四個(gè)全局隊(duì)列,避免帶來沒必要的麻煩。
創(chuàng)建和管理GCD隊(duì)列
dispatch_get_main_queue() -> dispatch_queue_t!
返回值
通過這個(gè)函數(shù)來獲取主線程隊(duì)列:
//返回主線程隊(duì)列 dispatch_get_main_queue()dispatch_get_global_queue(identifier: Int, _ flags: UInt) -> dispatch_queue_t!
返回值
通過這個(gè)函數(shù)來獲取系統(tǒng)提供的4個(gè)并發(fā)隊(duì)列
參數(shù)列表
| identifier | 代表的隊(duì)列的優(yōu)先級(jí),為DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW、DISPATCH_QUEUE_PRIORITY_BACKGROUND中的一個(gè) |
| flags | 蘋果官方解釋是保留已提供將來使用,目前讓這個(gè)參數(shù)為0 |
dispatch_queue_create(label: UnsafePointer, _ attr: dispatch_queue_attr_t!) -> dispatch_queue_t!
返回值
通過這函數(shù)來創(chuàng)建一個(gè)新的隊(duì)列。
參數(shù)列表
| label | 為你創(chuàng)建的隊(duì)列的標(biāo)簽,建議用反向DNS的命名風(fēng)格(com.example.myqueue),這個(gè)參數(shù)是可選的,可以為NULL。 |
| attr | GCD隊(duì)列的屬性,這個(gè)參數(shù)來選擇創(chuàng)建的是串行隊(duì)列(DISPATCH_QUEUE_SERIAL)還是并發(fā)隊(duì)列(DISPATCH_QUEUE_CONCURRENT)和隊(duì)列的優(yōu)先級(jí)別。 |
dispatch_get_current_queue()
返回值
返回當(dāng)前任務(wù)中的隊(duì)列。
dispatch_queue_attr_make_with_qos_class(attr: dispatch_queue_attr_t!, _ qos_class: dispatch_qos_class_t, _ relative_priority: Int32) -> dispatch_queue_attr_t!
返回值
返回一個(gè)適用于創(chuàng)建一個(gè)想要的服務(wù)質(zhì)量信息的GCD隊(duì)列的屬性。主要用于dispatch_queue_create函數(shù)。
參數(shù)列表
| attr | GCD隊(duì)列的屬性,這個(gè)參數(shù)來選擇創(chuàng)建的是串行隊(duì)列(DISPATCH_QUEUE_SERIAL)還是并發(fā)隊(duì)列(DISPATCH_QUEUE_CONCURRENT)和隊(duì)列的優(yōu)先級(jí)別。 |
| qos_class | 這個(gè)參數(shù)為隊(duì)列優(yōu)先級(jí),同樣為DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW、DISPATCH_QUEUE_PRIORITY_BACKGROUND中的一個(gè)。 |
| relative_priority | 這個(gè)參數(shù)為QOS類中相對(duì)優(yōu)先級(jí),這個(gè)值必須小于0,大于QOS_MIN_RELATIVE_PRIORITY,根據(jù)log數(shù)據(jù)發(fā)現(xiàn)QOS_MIN_RELATIVE_PRIORITY的值為-15,那說明第二個(gè)參數(shù)的值在0~-15之間。 |
第三個(gè)參數(shù)為QOS類中相對(duì)優(yōu)先級(jí),也就是第二個(gè)參數(shù)的類,這個(gè)值必須小于0,大于QOS_MIN_RELATIVE_PRIORITY。
global queue優(yōu)先級(jí)映射到以下quality-of-service類:
- DISPATCH_QUEUE_PRIORITY_HIG映射到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
dispatch_queue_get_label(dispatch_queue_t queue)
返回值
返回已經(jīng)創(chuàng)建隊(duì)列的指定標(biāo)簽。如果隊(duì)列在創(chuàng)建過程中沒有提供標(biāo)簽,則可能返回NULL。
//返回隊(duì)列的標(biāo)簽 dispatch_queue_get_label(dispatch_get_main_queue())dispatch_main( void)
執(zhí)行主隊(duì)列上被提交的所有block。這個(gè)函數(shù)是為主線程而存在的并且等待執(zhí)行提交到主隊(duì)列中的block。在主線程中調(diào)用了UIApplicationMain(iOS) ,NSApplicationMain(OS X),或者CFrunLoopRun的應(yīng)用程序一定不要調(diào)用dispatch_main。
int main(int argc, const charchar * argv[]) { @autoreleasepool { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"等待1。。。。"); }); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"等待1。。。。"); }); dispatch_main(); } return 0; }如上:如果不調(diào)用dispatch_main()函數(shù),則不會(huì)打印出結(jié)果。
dispatch_set_target_queue(object: dispatch_object_t!, _ queue: dispatch_queue_t!)
參數(shù)列表
| object | 要修改的對(duì)象,該參數(shù)不能為空。 |
| queue | 處理這個(gè)對(duì)象的目標(biāo)隊(duì)列,這個(gè)參數(shù)不能為NULL |
給GCD對(duì)象設(shè)置目標(biāo)隊(duì)列,這個(gè)目標(biāo)隊(duì)列負(fù)責(zé)處理這個(gè)對(duì)象。處理的對(duì)象分別有一下幾種:
GCD隊(duì)列:dispatch_queue_t,一個(gè)GCD隊(duì)列的優(yōu)先級(jí)是繼承自它的目標(biāo)隊(duì)列的。使用dispatch_get_global_queue函數(shù)去獲得一個(gè)合適的目標(biāo)隊(duì)列,這個(gè)目標(biāo)隊(duì)列就是你所需的優(yōu)先級(jí)。
如果你提交一個(gè)block到一個(gè)串行隊(duì)列中,并且這個(gè)串行隊(duì)列的目標(biāo)隊(duì)列是一個(gè)不同的串行隊(duì)列,那么這個(gè)block將不會(huì)與其他被提交到這個(gè)目標(biāo)隊(duì)列的block或者任何其他有相同目標(biāo)隊(duì)列的隊(duì)列同時(shí)調(diào)用。
GCD數(shù)據(jù)源:dispatch_source_t,為一個(gè)GCD數(shù)據(jù)源的目標(biāo)隊(duì)列指定了它的事件處理者的block和取消事件處理的block。
- GCD I/O通道:dispatch_io_t,一個(gè)GCD I/O通道的目標(biāo)隊(duì)列指定了被執(zhí)行的I/O操作。這可能會(huì)影響I/O操作結(jié)果的優(yōu)先級(jí)。例如,如果這個(gè)通道的目標(biāo)隊(duì)列的優(yōu)先級(jí)被設(shè)置為DISPATCH_QUEUE_PRIORITY_BACKGROUND,那么當(dāng)有I/O操作爭(zhēng)奪的時(shí)候,任何在這個(gè)隊(duì)列上通過dispatch_io_read或dispatch_io_write執(zhí)行的I/O操作都會(huì)被壓制。
關(guān)于dispatch_source_t和dispatch_io_t的用法會(huì)在以后的章節(jié)去介紹。
總結(jié):這篇文章主要詳細(xì)的介紹了一下關(guān)于多線程的知識(shí)和GCD隊(duì)列的的創(chuàng)建,關(guān)于GCD的用法和其他方面的知識(shí)會(huì)在后續(xù)文章里面去描述,給自己立一個(gè)flag,爭(zhēng)取完全弄懂以后寫出好的博客來。
參考文檔
蘋果官方GCD參考文檔
國(guó)外一篇好的關(guān)于GCD的文檔
總結(jié)
以上是生活随笔為你收集整理的GCD深入学习之GCD的初识的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++规律数列求和
- 下一篇: Accelio 代码笔记