GCD技術(shù)

什么是進(jìn)程?

最直觀的就是一個(gè)個(gè)pid,官方的說(shuō)法:進(jìn)程是程序在計(jì)算機(jī)上的一次執(zhí)行活動(dòng)。打開(kāi)一個(gè)app 就開(kāi)啟了一個(gè)進(jìn)程。可包含多個(gè)線程

什么是線程?

獨(dú)立執(zhí)行的代碼段,一個(gè)線程同一時(shí)間內(nèi)只能執(zhí)行一個(gè)任務(wù),反之多線程并發(fā)就可以在同一時(shí)間執(zhí)行多個(gè)任務(wù)。


同步和異步

一個(gè)同步函數(shù)只在完成了預(yù)定任務(wù)后才返回。會(huì)阻塞當(dāng)前線程。異步時(shí)任務(wù)開(kāi)啟會(huì)立即返回,不阻塞當(dāng)前線程去執(zhí)行下一個(gè)函數(shù)。異步會(huì)開(kāi)啟其他線程。


串行和并發(fā)

串行:任務(wù)按先后順序逐個(gè)執(zhí)行。并發(fā):后面的任務(wù)不會(huì)等前面的任務(wù)完成了再執(zhí)行,同樣會(huì)遵循先添加先執(zhí)行的原則,但添加間隔往往忽略不計(jì)。所以看上去像是一起執(zhí)行。


并發(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é),如下圖所示:




雖然你可以編寫(xiě)代碼在 GCD 下并發(fā)執(zhí)行,但 GCD 會(huì)決定有多少并行的需求。并行要求并發(fā),但并發(fā)并不能保證并行。


什么是GCD?

GCD 是一套低層API,用于將任務(wù)切分成單一任務(wù)提交至隊(duì)列并發(fā)或者串行執(zhí)行。遵循FIFO 原則,先提交到隊(duì)列的先執(zhí)行。串行隊(duì)列和并發(fā)隊(duì)列都是如此。


串行隊(duì)列

串行隊(duì)列中的任務(wù)一次執(zhí)行一個(gè),每個(gè)任務(wù)只在前一個(gè)任務(wù)完成時(shí)才開(kāi)始。而且,你不知道在一個(gè) Block 結(jié)束和下一個(gè)開(kāi)始之間的時(shí)間長(zhǎng)度,如下圖所示:




并發(fā)隊(duì)列

在并發(fā)隊(duì)列中的任務(wù)能得到的保證是它們會(huì)按照被添加的順序開(kāi)始執(zhí)行,但這就是全部的保證了。任務(wù)可能以任意順序完成,你不會(huì)知道何時(shí)開(kāi)始運(yùn)行下一個(gè)任務(wù),或者任意時(shí)刻有多少 Block 在運(yùn)行。再說(shuō)一遍,這完全取決于 GCD 。


下圖展示了一個(gè)示例任務(wù)執(zhí)行計(jì)劃,GCD 管理著四個(gè)并發(fā)任務(wù):




GCD基本隊(duì)列類型

1. Main quene

主線程隊(duì)列,串行,可以通過(guò)dispatch_get_main_quene() 獲取。UI操作都需要在主線程中執(zhí)行。


2. Global quene

系統(tǒng)提供的并發(fā)隊(duì)列。通過(guò)dispatch_get_global_queue 創(chuàng)建。


3. Custom quene

自定義隊(duì)列,可以為串行,也可為并發(fā)。通過(guò)dispatch_queue_create 創(chuàng)建。


隊(duì)列組

將多線程進(jìn)行分組,最大的好處是可獲知所有線程的完成情況。當(dāng)多線程并發(fā)執(zhí)行時(shí),由于單個(gè)線程什么時(shí)候結(jié)束并不知道,所以很難判斷線程組整個(gè)完成情況,通過(guò)dispatch_group_notify,可以直接監(jiān)聽(tīng)組里所有線程完成情況。


常規(guī)用法

1. Global quene 及 Custom quene(創(chuàng)建串行隊(duì)列)

1.1 并發(fā)隊(duì)列,異步執(zhí)行

此處為直接使用global_quene


override func viewDidLoad() { super.viewDidLoad() // 并發(fā)隊(duì)列,異步執(zhí)行 for index in 1...5 { dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in // println("currentIndex----\(index)") NSLog("currentIndex:----\(index), thread:---%@",NSThread.currentThread()) }) } NSLog("主線程,thread:---%@",NSThread.currentThread()) }



可看到執(zhí)行完成為無(wú)序的,而且每次都不一樣。同樣也能看到出主線程外,另外開(kāi)啟了5個(gè)線程。

注意此處用的是NSLog 輸出,而不是Println。因?yàn)镹SLog 本身是同步的,而Println 為異步,在多線程并發(fā)調(diào)用時(shí)Println 輸出結(jié)果會(huì)錯(cuò)亂。


1.2 并發(fā)隊(duì)列,同步執(zhí)行

還是上面的例子,緊改為同步執(zhí)行dispatch_sync


override func viewDidLoad() { super.viewDidLoad() // 并發(fā)隊(duì)列,同步執(zhí)行 for index in 1...5 { dispatch_sync(dispatch_get_global_queue(0, 0), { () -> Void in // println("currentIndex----\(index)") NSLog("currentIndex:----\(index), thread:---%@",NSThread.currentThread()) }) } NSLog("主線程:%@",[NSThread currentThread]);

可看到并沒(méi)有開(kāi)啟其他線程,任務(wù)按順序逐個(gè)執(zhí)行,同時(shí)阻塞主線程。搞不懂這種“并發(fā)隊(duì)列,同步執(zhí)行”的意義所在。


1.3 串行隊(duì)列,異步執(zhí)行

使用dispatch_quene_creat 創(chuàng)建串行隊(duì)列


override func viewDidLoad() { super.viewDidLoad() // 串行隊(duì)列,異步執(zhí)行 var quene = dispatch_queue_create("1", DISPATCH_QUEUE_SERIAL)// 創(chuàng)建串行隊(duì)列 for index in 1...5 { dispatch_async(quene, { () -> Void in // println("currentIndex----\(index)") NSLog("currentIndex:----\(index), thread:---%@",NSThread.currentThread()) }) } NSLog("主線程,thread:---%@",NSThread.currentThread()) }



可以看到另外開(kāi)啟了一個(gè)線程,不會(huì)將主線程阻塞,任務(wù)按順序執(zhí)行。


1.4 串行隊(duì)列,同步執(zhí)行

使用dispatch_quene_creat 創(chuàng)建串行隊(duì)列


override func viewDidLoad() { super.viewDidLoad() // 串行隊(duì)列,同步執(zhí)行 for index in 1...5 { dispatch_sync(quene, { () -> Void in // println("currentIndex----\(index)") NSLog("currentIndex:----\(index), thread:---%@",NSThread.currentThread()) }) } NSLog("主線程,thread:---%@",NSThread.currentThread()) }


可看到不會(huì)開(kāi)啟其他線程,會(huì)阻塞主線程,任務(wù)按順序執(zhí)行。


1.5 Custom quene 創(chuàng)建并發(fā)隊(duì)列

還是一樣的例子,只不過(guò)改為


var quene = dispatch_queue_create("1",DISPATCH_QUEUE_CONCURRENT)// 創(chuàng)建并發(fā)隊(duì)列

可看到異步,同步執(zhí)行結(jié)果與1.1 和1.2 一樣。就不一一列出了。


2. Main quene 使用,線程死鎖

想必這個(gè)應(yīng)該都知道怎么用,在其他線程中回到主線程,去執(zhí)行ui操作。注意是在其他線程中獲取主線程。所以要注意以下問(wèn)題。


2.1 不要在主線程中獲取主線程隊(duì)列,并同步執(zhí)行任務(wù)。

override func viewDidLoad() { dispatch_sync(dispatch_get_main_queue(), { () -> Void in NSLog("在主線程執(zhí)行任務(wù)") }) }

這種寫(xiě)法一定會(huì)線程死鎖。同步執(zhí)行首先就阻塞了主線程,然后又想在主線程去執(zhí)行任務(wù)所以任務(wù)沒(méi)法完成,任務(wù)沒(méi)法完成又導(dǎo)致了線程沒(méi)法結(jié)束。所以導(dǎo)致了惡性循環(huán),主線程就一直這么阻塞著。導(dǎo)致UI一直卡住。


3. 隊(duì)列組

3.1 使用場(chǎng)景

個(gè)人覺(jué)得先要知道什么時(shí)候需要使用到隊(duì)列組。隊(duì)列組一般配合dispatch_group_notify 使用,用于監(jiān)聽(tīng)這一組任務(wù)是否全部完成。所以使用場(chǎng)景為:


你要有多個(gè)任務(wù),如果是單個(gè)任務(wù)的情況,根本沒(méi)有必要使用隊(duì)列組。

而且還要是異步執(zhí)行的情況,若是同步阻塞在那執(zhí)行完了自然知道。

同時(shí)也不要認(rèn)為隊(duì)列組就會(huì)有很多隊(duì)列,其實(shí)不是,隊(duì)列組其實(shí)是要實(shí)現(xiàn)的是對(duì)線程所有任務(wù)的分組監(jiān)聽(tīng),所以只有一個(gè)隊(duì)列也可以


3.1.1異步執(zhí)行,串行隊(duì)列組

override func viewDidLoad() { super.viewDidLoad() var group = dispatch_group_create() var quene = dispatch_queue_create("1", DISPATCH_QUEUE_SERIAL)//串行隊(duì)列 dispatch_group_notify(group, quene) { () -> Void in dispatch_async(dispatch_get_main_queue(), { () -> Void in NSLog("任務(wù)結(jié)束,回到主線程") }) } for index in 1...5 { dispatch_group_async(group, quene, { () -> Void in NSLog("currentIndex:----\(index), thread:---%@",NSThread.currentThread()) }) } NSLog("主線程") }

可看到除主線程外,有其他一個(gè)線程,串行任務(wù)結(jié)束后能夠?qū)崟r(shí)監(jiān)聽(tīng)到,回到主線程。


3.1.2異步執(zhí)行,并發(fā)隊(duì)列組

override func viewDidLoad() { super.viewDidLoad() var group = dispatch_group_create() var quene = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT)//并發(fā)隊(duì)列,也可以用global_quene dispatch_group_notify(group, quene) { () -> Void in dispatch_async(dispatch_get_main_queue(), { () -> Void in NSLog("任務(wù)結(jié)束,回到主線程") }) } for index in 1...5 { dispatch_group_async(group, quene, { () -> Void in NSLog("currentIndex:----\(index), thread:---%@",NSThread.currentThread()) }) } NSLog("主線程") }


同樣所有線程的任務(wù)全部結(jié)束后,能監(jiān)聽(tīng)到,通知主線程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容