一、GCD簡介
Grand Central Dispatch(宏偉的中心調(diào)度隊(duì)列楚堤,不知道這樣翻譯對(duì)不對(duì)),它是蘋果為多核的并行運(yùn)算提出的解決方案,所以會(huì)自動(dòng)合理地利用更多的 CPU 內(nèi)核(比如雙核及老、四核)抽莱,最重要的是它會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)骄恶、銷毀線程)食铐,完全不需要我們管理,我們只需要告訴干什么就行叠蝇。同時(shí)它使用的也是C語言璃岳,不過由于使用了閉包,使得使用起來更加方便悔捶、靈活铃慷。在 Swift 3 中,蘋果對(duì) GCD 做了大量改變蜕该,使其更加面向?qū)ο罄绻瘢牡拿婺咳橇耍员疚闹攸c(diǎn)在 Swift3 中的 GCD堂淡。
二馋缅、任務(wù)和隊(duì)列
GCD中有2個(gè)核心概念:任務(wù)和隊(duì)列。
任務(wù):執(zhí)行什么操作
在 GCD 中就是一個(gè) Block绢淀,所以添加任務(wù)十分方便萤悴。任務(wù)有兩種執(zhí)行方式: 同步執(zhí)行和異步執(zhí)行,他們之間的區(qū)別是是否會(huì)創(chuàng)建新的線程皆的。
同步(sync)
:會(huì)阻塞當(dāng)前線程覆履,直到 Block 中的任務(wù)執(zhí)行完畢!注意這里這里是指阻塞當(dāng)前線程费薄,如果是阻塞子線程硝全,界面并不會(huì)卡住,因?yàn)閁I更新都在主線程楞抡;
異步(async)
:當(dāng)前線程會(huì)直接往下執(zhí)行伟众,它不會(huì)阻塞當(dāng)前線程。
注意:同步(sync)
一定不會(huì)開啟新線程召廷,異步(async)
可能會(huì)開啟新線程凳厢。隊(duì)列:用來存放任務(wù)
串行隊(duì)列
:放到串行隊(duì)列的任務(wù),GCD 會(huì) FIFO(先進(jìn)先出)地取出來一個(gè)柱恤,執(zhí)行一個(gè)数初,然后取下一個(gè),這樣一個(gè)一個(gè)的執(zhí)行梗顺。
并行隊(duì)列
:放到并行隊(duì)列的任務(wù)泡孩,GCD 也會(huì) FIFO 的取出來,但不同的是寺谤,它取出來一個(gè)就會(huì)放到別的線程仑鸥,然后再取出來另一個(gè)吮播,放到另一個(gè)的線程。這樣由于取的動(dòng)作很快眼俊,忽略不計(jì)意狠,看起來,所有的任務(wù)都是一起執(zhí)行的疮胖,實(shí)際上环戈,一個(gè) CPU(單核),在一個(gè)時(shí)間點(diǎn)澎灸,只能開啟一個(gè)線程院塞,并行是說 CPU 在多個(gè)線程之間快速來回切換,看上去像多個(gè)線程一起在執(zhí)行性昭。只有多核 CPU 才能真正意義上的并行拦止。
注意:雖然 GCD 遵循 FIFO 的順序,但在并行時(shí)糜颠,哪一條線程先執(zhí)行完畢是不確定的汹族。例如:A 隊(duì)列第一個(gè)加入任務(wù),B 隊(duì)列第二個(gè)加入任務(wù)其兴,GCD 一定是先取出A隊(duì)列顶瞒,后取出B隊(duì)列。但有可能是 B 隊(duì)列先執(zhí)行完元旬,A 隊(duì)列后執(zhí)行完搁拙,這樣就會(huì)看到先輸出 B,后輸出 A法绵。
同步執(zhí)行 | 異步執(zhí)行 | |
---|---|---|
串行隊(duì)列 | 當(dāng)前線程,一個(gè)一個(gè)執(zhí)行 | 另開一條線程酪碘,一個(gè)一個(gè)執(zhí)行 |
并行隊(duì)列 | 當(dāng)前線程朋譬,一個(gè)一個(gè)執(zhí)行 | 開很多線程,一起執(zhí)行 |
注意:不能獲取同步主隊(duì)列
三兴垦、基本使用
- 全局異步隊(duì)列
DispatchQueue.global().async {
//耗時(shí)操作
}
- 主隊(duì)列
DispatchQueue.main.async {
//刷新UI
}
- 延遲操作
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
// 2秒后執(zhí)行
}
- 隊(duì)列組
隊(duì)列組就是把任務(wù)放在DispatchGroup
中(入組)徙赢,當(dāng)任務(wù)執(zhí)行完畢時(shí)(出組),即當(dāng)DispatchGroup
中沒有任務(wù)時(shí)探越,調(diào)用監(jiān)聽方法notify
狡赐,注意:入組和出組一定要成對(duì)出現(xiàn),有幾個(gè)入組钦幔,就一定需要有幾個(gè)出組枕屉。
// 創(chuàng)建一個(gè)隊(duì)列組
let group = DispatchGroup()
// A任務(wù)入組
group.enter()
// A任務(wù)異步操作
DispatchQueue.global().async(group: group, execute: DispatchWorkItem(block: {
sleep(2)
print("download task A ...")
// 出組
group.leave()
}))
// B任務(wù)入組
group.enter()
// B任務(wù)異步操作
DispatchQueue.global().async(group: group, execute: DispatchWorkItem(block: {
sleep(2)
print("download task B ...")
// 出組
group.leave()
}))
// 主線程監(jiān)聽,只有當(dāng)隊(duì)列組中沒有任務(wù)鲤氢,才會(huì)執(zhí)行閉包搀擂。如果多次調(diào)用該方法西潘,每次都會(huì)去檢查隊(duì)列組中是否有任務(wù),如果沒有任務(wù)才執(zhí)行
group.notify(queue: DispatchQueue.main) {
print("complete!")
}
運(yùn)行程序哨颂,打印如下:
因?yàn)?task A 和 task B 是加在全局隊(duì)列中喷市,所以它們的執(zhí)行順序由系統(tǒng)決定。但 complete 一定是在兩個(gè)任務(wù)完成后才執(zhí)行威恼。