GCD的優(yōu)勢:
- GCD 可用于多核的并行運(yùn)算
- GCD 會自動(dòng)利用更多的 CPU 內(nèi)核(比如雙核、四核)
- GCD會自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
- 程序員只需要告訴 GCD 想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
** GCD 任務(wù)和隊(duì)列**
任務(wù):在線程中執(zhí)行操作代碼,在 GCD 中是放在 block 中的。
執(zhí)行任務(wù)有兩種方式:
同步執(zhí)行(sync)和異步執(zhí)行(async)。
同步執(zhí)行(sync):
同步添加任務(wù)到指定的隊(duì)列中,在添加的任務(wù)執(zhí)行結(jié)束之前,會一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行。
只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力。
異步執(zhí)行(async):
異步添加任務(wù)到指定的隊(duì)列中,它不會做任何等待,可以繼續(xù)執(zhí)行任務(wù)。
可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力。
隊(duì)列(Dispatch Queue):
隊(duì)列指執(zhí)行任務(wù)的等待隊(duì)列,即用來存放任務(wù)的隊(duì)列。隊(duì)列是一種特殊的線性表,采用 FIFO(先進(jìn)先出)
串行隊(duì)列(Serial Dispatch Queue):
每次只有一個(gè)任務(wù)被執(zhí)行。讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行。(只開啟一個(gè)線程,一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))
并發(fā)隊(duì)列(Concurrent Dispatch Queue):
可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行。(可以開啟多個(gè)線程,并且同時(shí)執(zhí)行任務(wù))
并發(fā)隊(duì)列的并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
GCD 的使用步驟
- 創(chuàng)建一個(gè)隊(duì)列(串行隊(duì)列或并發(fā)隊(duì)列)
- 將任務(wù)追加到任務(wù)的等待隊(duì)列中,系統(tǒng)會根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或異步執(zhí)行)
特別注意
- 在主線程中調(diào)用同步執(zhí)行 + 主隊(duì)列 = 死鎖
- 在子線程中調(diào)用同步執(zhí)行 + 主隊(duì)列 = 串行執(zhí)行
GCD 線程間的通信
在其他線程中先執(zhí)行任務(wù),執(zhí)行完了之后回到主線程執(zhí)行主線程的相應(yīng)操作
GCD 的其他方法
GCD 柵欄方法:dispatch_barrier_async
函數(shù)會等待前邊追加到并發(fā)隊(duì)列中的任務(wù)全部執(zhí)行完畢之后,再將指定的任務(wù)追加到該異步隊(duì)列中GCD 延時(shí)執(zhí)行方法:dispatch_after
在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中。但時(shí)間并不精確GCD 一次性代碼(只執(zhí)行一次):dispatch_once
在創(chuàng)建單例、或者有整個(gè)程序運(yùn)行過程中只執(zhí)行一次的代碼時(shí),就用到了 GCD 的這個(gè)函數(shù),能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次,并且即使在多線程的環(huán)境下,也可以保證線程安全。GCD 快速迭代方法:dispatch_apply
按照指定的次數(shù)將指定的任務(wù)追加到指定的隊(duì)列中,并等待全部隊(duì)列執(zhí)行結(jié)束。串行隊(duì)列中使用 dispatch_apply,就和for循環(huán)一樣,按順序同步執(zhí)行。
異步并行隊(duì)列使用,在多個(gè)線程中同時(shí)執(zhí)行,順序不可控。
GCD 隊(duì)列組:dispatch_group
分別異步執(zhí)行2個(gè)耗時(shí)任務(wù),然后當(dāng)2個(gè)耗時(shí)任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)。這時(shí)候我們可以用到 GCD 的隊(duì)列組。調(diào)用隊(duì)列組的 dispatch_group_async 先把任務(wù)放到隊(duì)列中,然后將隊(duì)列放入隊(duì)列組中?;蛘呤褂藐?duì)列組的 dispatch_group_enter、dispatch_group_leave組合 來實(shí)現(xiàn)dispatch_group_async。
調(diào)用隊(duì)列組的 dispatch_group_notify 回到指定線程執(zhí)行任務(wù)。或者使用 dispatch_group_wait 回到當(dāng)前線程繼續(xù)向下執(zhí)行(會阻塞當(dāng)前線程)。
dispatch_group_notify
監(jiān)聽 group 中任務(wù)的完成狀態(tài),當(dāng)所有的任務(wù)都執(zhí)行完成后,才執(zhí)行dispatch_group_notify block中追加的任務(wù)。
dispatch_group_wait
暫停當(dāng)前線程(阻塞當(dāng)前線程),等待指定的 group 中的任務(wù)執(zhí)行完成后,才會往下繼續(xù)執(zhí)行。
使用dispatch_group_wait 會阻塞當(dāng)前線程。
dispatch_group_enter、dispatch_group_leave
dispatch_group_enter 標(biāo)志著一個(gè)任務(wù)追加到 group,執(zhí)行一次,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)+1
dispatch_group_leave 標(biāo)志著一個(gè)任務(wù)離開了 group,執(zhí)行一次,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)-1。
當(dāng) group 中未執(zhí)行完畢任務(wù)數(shù)為0的時(shí)候,才會使dispatch_group_wait解除阻塞,以及執(zhí)行追加到dispatch_group_notify中的任務(wù)。
dispatch_group_enter、dispatch_group_leave組合,其實(shí)等同于dispatch_group_async。
GCD 信號量:dispatch_semaphore
GCD中的信號量是指 Dispatch Semaphore,是持有計(jì)數(shù)的信號。
計(jì)數(shù)為0時(shí)等待,不可通過。
計(jì)數(shù)為1或大于1時(shí),計(jì)數(shù)減1且不等待,可通過。
Dispatch Semaphore 提供了三個(gè)函數(shù)。
- dispatch_semaphore_create:創(chuàng)建一個(gè)Semaphore并初始化信號的總量
- dispatch_semaphore_signal:發(fā)送一個(gè)信號,讓信號總量加1
- dispatch_semaphore_wait:可以使總信號量減1,當(dāng)信號總量為0時(shí)就會一直等待(阻塞所在線程),否則就可以正常執(zhí)行。
注意:信號量的使用前提是:想清楚你需要處理哪個(gè)線程等待(阻塞),又要哪個(gè)線程繼續(xù)執(zhí)行,然后使用信號量。
Dispatch Semaphore 線程同步
- 保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
- 保證線程安全,為線程加鎖
異步執(zhí)行耗時(shí)任務(wù),并使用異步執(zhí)行的結(jié)果進(jìn)行一些額外的操作。== 將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。
AFURLSessionManager.m 里面的 tasksForKeyPath: 方法。通過引入信號量的方式,等待異步執(zhí)行任務(wù)結(jié)果,獲取到 tasks,然后再返回該 tasks。
線程安全(使用 semaphore 加鎖)
semaphore 數(shù)為1線程安全加鎖
dispatch_semaphore_create 創(chuàng)建一個(gè)信號量,傳入數(shù)字為信號總量。到0位鎖
dispatch_semaphore_signal 信號量+1 相當(dāng)于解鎖
dispatch_semaphore_wait 信號量-1 相當(dāng)于加鎖