GCD(Swift)

原文連接前端

這篇文主要想總結下多線程在swift中的使用,先看下基本概念swift

進程


進程指在系統中能獨立運行並做爲資源分配的基本單位,它是由一組機器指令、數據和堆棧等組成的,是一個能獨立運行的活動實體後端

線程


線程是進程的基本執行單元,一個進程(程序)的全部任務都在線程中執行。數組

隊列


隊列,又稱爲佇列(queue),是先進先出(FIFO, First-In-First-Out)的線性表。在具體應用中一般用鏈表或者數組來實現。隊列只容許在後端(稱爲rear)進行插入操做,在前端(稱爲front)進行刪除操做。隊列的操做方式和堆棧相似,惟一的區別在於隊列只容許新數據在後端進行添加。安全

同步/異步


能夠這麼理解:(知乎)多線程

  • 假如你要作兩件事 , 燒水 、 刷牙
  • 同步 :你燒水 , 等水燒開了你再去刷牙
  • 異步 :你燒水 ,不等水燒開就去刷牙了 , 水燒開了會發出聲音告訴你(callback) , 而後你再處理水燒開以後的事情

只要你是個正常人 , 都會選擇第二種 , 固然也有特殊狀況 , 那我的喜歡用熱水刷牙併發

併發


指兩個或多個事件在同一時間間隔內發生。能夠在某條線程和其餘線程之間反覆屢次進行上下文切換,看上去就好像一個CPU可以而且執行多個線程同樣。實際上是僞異步。異步

線程隊列中並行/串行


串行隊列:串行隊列的特色是隊列內的線程是一個一個執行,直到結束。並行隊列:並行隊列的特色是隊列中全部線程的執行結束時必須是一塊的,隊列中其餘線程執行完畢後,會阻塞當前線程等待隊列中其餘線程執行,而後一塊執行完畢。async


如何使用


DispatchQueue.global().async {

    print("do something in global \(Thread.current)")

    DispatchQueue.main.async {

        print("do something in main \(Thread.current)")
    }
}
複製代碼

這裏使用了全局的隊列執行一些任務 , 而後切回主隊列 , 這裏要注意主隊列是運行在主線程上的任務堆棧 。性能

自定義隊列

除了使用全局隊列外咱們還可使用自定義的隊列

let q = DispatchQueue(label: "com.felix.felix")
複製代碼

初始化一個隊列最簡單的方式就是聲明它的標籤 。

async

打開Xcode,新建一個commandLineTool工程、 打開main.swift

let q = DispatchQueue(label: "com.felix.felix")

q.sync {
    (1...5).forEach({ i in
        print("🍎 \(Thread.current) + \(i)")
    })
}
q.async {
    (6...10).forEach({ i in
        print("🍇 \(Thread.current) + \(i)")
    })
}
(11...15).forEach({ i in
    print("🍌 \(Thread.current) + \(i)")
})
複製代碼

先聲明一個隊列,使用sync添加一個同步的任務輸出1到5,使用async異步輸出6到10,同時在主線程打印11到15 。

按下command+R運行project,

🍎 <NSThread: 0x103103480>{number = 1, name = main} + 1
🍎 <NSThread: 0x103103480>{number = 1, name = main} + 2
🍎 <NSThread: 0x103103480>{number = 1, name = main} + 3
🍎 <NSThread: 0x103103480>{number = 1, name = main} + 4
🍎 <NSThread: 0x103103480>{number = 1, name = main} + 5
🍌 <NSThread: 0x103103480>{number = 1, name = main} + 11
🍇 <NSThread: 0x103007940>{number = 2, name = (null)} + 6
🍌 <NSThread: 0x103103480>{number = 1, name = main} + 12
🍇 <NSThread: 0x103007940>{number = 2, name = (null)} + 7
🍌 <NSThread: 0x103103480>{number = 1, name = main} + 13
🍇 <NSThread: 0x103007940>{number = 2, name = (null)} + 8
🍌 <NSThread: 0x103103480>{number = 1, name = main} + 14
🍇 <NSThread: 0x103007940>{number = 2, name = (null)} + 9
🍌 <NSThread: 0x103103480>{number = 1, name = main} + 15
🍇 <NSThread: 0x103007940>{number = 2, name = (null)} + 10
Program ended with exit code: 0
複製代碼

咱們能夠看到,🍎表明的任務所有是優先執行的,這說明sync添加的任務會阻塞當前線程,在看到🍌和🍇是均勻分部的,這是因爲async添加的任務會默認加入由系統管理的線程池,異步執行 。

優先級 QoS


當多個隊列同時執行的時候,系統須要知道哪一個隊列優先級更高,才能優先安排計算資源給他,咱們能夠這樣定義優先級:

let q = DispatchQueue(label: "com.felix.felix", qos: DispatchQoS.background)
複製代碼

初始化的時候加上qos參數 , qos(quality of service)從字面上理解就是「服務質量」,在swift中是這樣定義的:

public enum QoSClass {

        @available(OSX 10.10, iOS 8.0, *)
        case background

        @available(OSX 10.10, iOS 8.0, *)
        case utility

        @available(OSX 10.10, iOS 8.0, *)
        case `default`

        @available(OSX 10.10, iOS 8.0, *)
        case userInitiated

        @available(OSX 10.10, iOS 8.0, *)
        case userInteractive

        case unspecified

        @available(OSX 10.10, iOS 8.0, *)
        public init?(rawValue: qos_class_t)

        @available(OSX 10.10, iOS 8.0, *)
        public var rawValue: qos_class_t { get }
    }
複製代碼

  • User Interactive: 和用戶交互相關,好比動畫等等優先級最高。好比用戶連續拖拽的計算
  • User Initiated: 須要馬上的結果,好比push一個ViewController以前的數據計算
  • Utility: 能夠執行很長時間,再通知用戶結果。好比下載一個文件,給用戶下載進度。
  • Background: 用戶不可見,好比在後臺存儲大量數據

在選擇優先級時能夠參考以下判斷 。

  • 這個任務是用戶可見的嗎?
  • 這個任務和用戶交互有關嗎?
  • 這個任務的執行時間有多少?
  • 這個任務的最終結果和UI有關係嗎?

併發隊列


默認狀況下添加進Queue的任務會串行執行 , 先執行完一個再執行下一個:

import Foundation

let q = DispatchQueue(label: "com.felix.felix")

q.async {
    (1...5).forEach({ i in
        print("🍎 \(Thread.current) + \(i)")
    })
}
q.async {
    (6...10).forEach({ i in
        print("🍇 \(Thread.current) + \(i)")
    })
}
(11...15).forEach({ i in
    print("🍌 \(Thread.current) + \(i)")
})
複製代碼

運行看下日誌輸出

🍎 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 1
🍌 <NSThread: 0x100f046f0>{number = 1, name = main} + 11
🍎 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 2
🍌 <NSThread: 0x100f046f0>{number = 1, name = main} + 12
🍎 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 3
🍌 <NSThread: 0x100f046f0>{number = 1, name = main} + 13
🍎 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 4
🍌 <NSThread: 0x100f046f0>{number = 1, name = main} + 14
🍎 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 5
🍌 <NSThread: 0x100f046f0>{number = 1, name = main} + 15
🍇 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 6
🍇 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 7
🍇 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 8
🍇 <NSThread: 0x102a081a0>{number = 2, name = (null)} + 9
Program ended with exit code: 0
複製代碼

咱們能夠看到直到🍎都輸出完畢纔會輸出🍇,有時候咱們想把任務並行執行,怎麼辦呢。 能夠設置queue的Attributes。

let q = DispatchQueue(label: "com.felix.felix", attributes: DispatchQueue.Attributes.concurrent)
複製代碼

再運行下看看會怎樣。

DispatchWorkItem


有的時候,對於同一個操做咱們有可能會放在不一樣的線程中去執行,這樣咱們就能夠把這個操做用DispatchWorkItem的形式包裹起來,在不一樣的線程中執行 。

import Foundation

let group = DispatchGroup()

let q = DispatchQueue(label: "com.felix.felix", attributes: DispatchQueue.Attributes.concurrent)

let item1 = DispatchWorkItem {
    (1...5).forEach({ i in
        print("🍎 \(Thread.current) + \(i)")
    })
}

let item2 = DispatchWorkItem {
    (6...10).forEach({ i in
        print("🍇 \(Thread.current) + \(i)")
    })
}


q.async(execute: item1)

q.async(execute: item2)

(11...15).forEach({ i in
    print("🍌 \(Thread.current) + \(i)")
})
複製代碼

Group 隊列組

DispatchGroup 能夠用來管理一組隊列,監聽全部隊列的全部任務都完成的通知,比較經常使用的就是在一個頁面請求多個接口的時候,所有請求完再刷新UI 。

總結

總之,使用GCD一方面會提高咱們應用的性能,給用戶帶來更好的體驗,不過必定要注意線程安全問題。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息