iOS 多線程—GCD 基本用法

什麼是進程?

最通俗的描述就是一個個pid,官方的說法:進程是程序在計算機上的一次執行活動。打開一個app 就開啓了一個進程。可包含多個線程。 輸入圖片說明多線程

什麼是線程?

獨立執行的代碼段,一個線程同一時間內只能執行一個任務,反之多線程併發就能夠在同一時間執行多個任務。併發

同步和異步

一個同步函數只在完成了預約任務後才返回。會阻塞當前線程。異步時任務開啓會當即返回,不阻塞當前線程去執行下一個函數。異步會開啓其餘線程。app

串行和併發

串行:任務按前後順序逐個執行。併發:後面的任務不會等前面的任務完成了再執行,一樣會遵循先添加先執行的原則,但添加間隔每每忽略不計。因此看上去像是一塊兒執行。異步

併發與並行

併發和並行一般被一塊兒提到,因此值得花些時間解釋它們之間的區別。async

併發代碼的不一樣部分能夠「同步」執行。然而,該怎樣發生或是否發生都取決於系統。多核設備經過並行來同時執行多個線程;然而,爲了使單核設備也能實現這一點,它們必須先運行一個線程,執行一個上下文切換,而後運行另外一個線程或進程。這一般發生地足夠快以至給咱們併發執行地錯覺,以下圖所示:ide

輸入圖片說明 雖然你能夠編寫代碼在 GCD 下併發執行,但 GCD 會決定有多少並行的需求。並行要求併發,但併發並不能保證並行。函數

什麼是GCD?

GCD 是一套低層API,用於將任務切分紅單一任務提交至隊列併發或者串行執行。遵循FIFO 原則,先提交到隊列的先執行。串行隊列和併發隊列都是如此。ui

串行隊列

串行隊列中的任務一次執行一個,每一個任務只在前一個任務完成時纔開始。並且,你不知道在一個 Block 結束和下一個開始之間的時間長度,以下圖所示: 輸入圖片說明spa

併發隊列

在併發隊列中的任務能獲得的保證是它們會按照被添加的順序開始執行,但這就是所有的保證了。任務可能以任意順序完成,你不會知道什麼時候開始運行下一個任務,或者任意時刻有多少 Block 在運行。再說一遍,這徹底取決於 GCD 。線程

下圖展現了一個示例任務執行計劃,GCD 管理着四個併發任務:

輸入圖片說明

GCD基本隊列類型

1. Main quene

主線程隊列,串行,能夠經過dispatch_get_main_quene() 獲取。UI操做都須要在主線程中執行。 ###2. Global quene 系統提供的併發隊列。經過dispatch_get_global_queue 建立。 ###3. Custom quene 自定義隊列,能夠爲串行,也可爲併發。經過dispatch_queue_create 建立。

隊列組

將多線程進行分組,最大的好處是可獲知全部線程的完成狀況。當多線程併發執行時,因爲單個線程何時結束並不知道,因此很難判斷線程組整個完成狀況,經過dispatch_group_notify,能夠直接監聽組裏全部線程完成狀況。

常規用法

1. Global quene 及 Custom quene(建立串行隊列)

####1.1 併發隊列,異步執行 此處爲直接使用global_quene

    override func viewDidLoad() {
        super.viewDidLoad()
        // 併發隊列,異步執行
        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())
    }

NSLog打印結果爲: 輸入圖片說明

可看到執行完成爲無序的,並且每次都不同。一樣也能看到出主線程外,另外開啓了5個線程。 注意此處用的是NSLog 輸出,而不是Println。由於NSLog 自己是同步的,而Println 爲異步,在多線程併發調用時Println 輸出結果會錯亂。

####1.2 併發隊列,同步執行 仍是上面的例子,緊改成同步執行dispatch_sync

    override func viewDidLoad() {
        super.viewDidLoad()
        // 併發隊列,同步執行
        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("主線程,thread:---%@",NSThread.currentThread())
    }

打印結果爲:

輸入圖片說明 可看到並無開啓其餘線程,任務按順序逐個執行,同時阻塞主線程。搞不懂這種「併發隊列,同步執行」的意義所在。

####1.3 串行隊列,異步執行 使用dispatch_quene_creat 建立串行隊列

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

打印結果爲: 輸入圖片說明

能夠看到另外開啓了一個線程,不會將主線程阻塞,任務按順序執行。

####1.4 串行隊列,同步執行 使用dispatch_quene_creat 建立串行隊列

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

打印結果爲: 輸入圖片說明 可看到不會開啓其餘線程,會阻塞主線程,任務按順序執行。

1.5 Custom quene 建立併發隊列

仍是同樣的例子,只不過改成

        var quene  = dispatch_queue_create("1",DISPATCH_QUEUE_CONCURRENT)// 建立併發隊列

可看到異步,同步執行結果與1.1 和1.2 同樣。就不一一列出了。

###2. Main quene 使用,線程死鎖 想必這個應該都知道怎麼用,在其餘線程中回到主線程,去執行ui操做。注意是在其餘線程中獲取主線程。因此要注意如下問題。 ####2.1 不要在主線程中獲取主線程隊列,並同步執行任務。

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

這種寫法必定會線程死鎖。同步執行首先就阻塞了主線程,而後又想在主線程去執行任務因此任務無法完成,任務無法完成又致使了線程無法結束。因此致使了惡性循環,主線程就一直這麼阻塞着。致使UI一直卡住。

###3. 隊列組 ####3.1 使用場景 我的以爲先要知道何時須要使用到隊列組。隊列組通常配合dispatch_group_notify 使用,用於監聽這一組任務是否所有完成。因此使用場景爲:

  • 你要有多個任務,若是是單個任務的狀況,根本沒有必要使用隊列組。
  • 並且還要是異步執行的狀況,如果同步阻塞在那執行完了天然知道。

同時也不要認爲隊列組就會有不少隊列,其實不是,隊列組實際上是要實現的是對線程全部任務的分組監聽,因此只有一個隊列也能夠

3.1.1異步執行,串行隊列組

    override func viewDidLoad() {
        super.viewDidLoad()
        var group = dispatch_group_create()
        var quene = dispatch_queue_create("1", DISPATCH_QUEUE_SERIAL)//串行隊列

        dispatch_group_notify(group, quene) { () -> Void in
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                NSLog("任務結束,回到主線程")
            })
        }
        for index in 1...5 {
            dispatch_group_async(group, quene, { () -> Void in
                NSLog("currentIndex:----\(index), thread:---%@",NSThread.currentThread())
            })
        }
        NSLog("主線程")
    }

打印結果爲:

輸入圖片說明 可看到除主線程外,有其餘一個線程,串行任務結束後可以實時監聽到,回到主線程。

3.1.2異步執行,併發隊列組

    override func viewDidLoad() {
        super.viewDidLoad()
        var group = dispatch_group_create()
        var quene = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT)//併發隊列,也能夠用global_quene
        dispatch_group_notify(group, quene) { () -> Void in
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                NSLog("任務結束,回到主線程")
            })
        }
        for index in 1...5 {
            dispatch_group_async(group, quene, { () -> Void in
                NSLog("currentIndex:----\(index), thread:---%@",NSThread.currentThread())
            })
        }
        NSLog("主線程")
    }

打印結果爲:

輸入圖片說明 一樣全部線程的任務所有結束後,能監聽到,通知主線程。

##寫在最後 本文憑藉我的理解,同時也參考了很多博客。列舉了GCD的一些常規用法,還有不少特性沒有一一列出。不熟的知識點不敢亂寫,之後再慢慢完善。iOS 多線程也不只僅是GCD 這一種,但我以爲最好用的仍是GCD。以爲有用就打賞,點贊,同時有錯誤忘指正...

參考:Grand Central Dispatch In-Depth

相關文章
相關標籤/搜索