什么是進(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)到,通知主線程。