https://www.jianshu.com/p/47e45367e524編程
在Swift4.0版本中GCD的經常使用方法仍是有比較大的改動,這裏作個簡單的整理彙總。swift
隊列是一種遵循先進先出(FIFO)原則的數據結構,是一種特殊的線性表。安全
主隊列 | 全局隊列 | 串行隊列 | 並行隊列 | |
---|---|---|---|---|
同步 | X | 並行同步 | 並行同步 | |
異步 | 串行異步 | 並行異步 | 串行異步 | 並行異步 |
X 表示禁止這麼使用,—— 表示不建議這麼使用。bash
主隊列默認是串行的,另外主隊列不能結合同步函數(sync)使用,會形成線程死鎖。網絡
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let mainQueue = DispatchQueue.main mainQueue.sync { print("形成當前線程:\(Thread.current)死鎖") } }
同時主隊列中不該該添加耗時的任務,由於系統的UI相關事務都是在主線程隊列中完成的,大量大耗時操做可能會形成卡頓,應該避免。數據結構
主隊列最經常使用的方法是當子線程須要通知主線程作一些UI上面的操做時,結合子線程使用:多線程
let queue = DispatchQueue(label: "com.roki.thread") queue.async { // 大量耗時操做 print("大量耗時操做線程:\(Thread.current)") Thread.sleep(forTimeInterval: 2) DispatchQueue.main.async { //回到主線程操做UI print("回到主線程:\(Thread.current)") } }
全局隊列是由系統建立的,默認是並行的。全局隊列具體運行在哪個線程,是由系統維護一個線程池,而後挑選其中的一至多條線程來使用。哪條線程會被使用是未知的,是由系統根據當前的併發任務,處理器的負載等狀況來決定的。閉包
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. for i in 1...10 { DispatchQueue.global().sync { //全局併發同步 Thread.sleep(forTimeInterval: 2) print("線程\(Thread.current)正在執行\(i)號任務") } } }
從終端輸出咱們能夠知道任務被順序執行了,這是由於雖然當前是一個併發隊列,可是是同步執行的。同步操做會使得在前一個任務完成後纔去執行下一個任務。同步與異步的區別還在於它不會建立新的線程,而是直接在當前線程中執行了相關的任務,當前線程是主線程。同步會阻塞當前線程。併發
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. for i in 1...10 { DispatchQueue.global().async { //全局併發異步 Thread.sleep(forTimeInterval: 2) print("線程\(Thread.current)正在執行\(i)號任務") } } }
從終端輸出咱們能夠知道任務被隨機執行了,並且被分配在多個子線程中執行的,這符合併發的本質。另外須要注意的是全局併發異步隊列,系統在挑選來執行任務的線程的時候,會挑選除了主線程以外的其餘線程。異步
除了上述隊列以外,咱們還可使用DispatchQueue建立自定義的隊列。let queue = DispatchQueue(label: "com.roki.thread")
須要注意的是上述建立自定義隊列的方式,默認建立的是串行隊列。
還有一種建立自定義隊列的方法是:
let queue = DispatchQueue(label: "com.custom.thread", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent)
iOS10.0 以後上述API更新爲:
let queue = DispatchQueue(label: "com.custom.thread", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: .workItem, target: nil)
參數說明:
label 表示隊列標籤
qos 表示設置隊列的優先級
attributes 表示隊列類型,默認爲串行隊列,設置爲.concurrent表示並行隊列。iOS 10.0以後 attributes 新增.initiallyInactive屬性表示當前隊列是不活躍的,它須要調用DispatchQueue的activate方法來執行任務。
autoreleaseFrequency 表示自動釋放頻率,設置自動釋放機制。
let queue = DispatchQueue(label: "com.custom.thread") queue.sync { //同步串行隊列 }
let queue = DispatchQueue(label: "com.custom.thread") queue.async { //異步串行隊列 }
根據iOS10.0 以後attributes新增的.initiallyInactive屬性,咱們能夠建立不活躍隊列。
let queue = DispatchQueue(label: "com.custom.thread", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.initiallyInactive, autoreleaseFrequency: .workItem, target: nil) queue.sync { //同步串行不活躍隊列 } queue.activate()
let queue = DispatchQueue(label: "com.custom.thread", qos: DispatchQoS.default, attributes: [.initiallyInactive, .concurrent], autoreleaseFrequency: .workItem, target: nil) queue.async { //異步並行不活躍隊列 } queue.activate()
let queue = DispatchQueue(label: "com.custom.thread", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent) for i in 1...10 { queue.sync { //併發同步隊列 Thread.sleep(forTimeInterval: 2) print("線程\(Thread.current)正在執行\(i)號任務") } }
let queue = DispatchQueue(label: "com.custom.thread", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent) for i in 1...10 { queue.async { //併發異步 Thread.sleep(forTimeInterval: 2) print("線程\(Thread.current)正在執行\(i)號任務") } }
若是咱們想監聽多個任務的執行狀況,那麼咱們須要將任務(異步、同步、串行、並行)都添加到任務組中,而後經過DispatchGroup的notify函數就能夠監聽是否組內任務都已經完成。
let group = DispatchGroup() let queue = DispatchQueue(label: "com.custom.thread", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent) for i in 1...10 { queue.async(group: group) { //併發異步 Thread.sleep(forTimeInterval: 2) print("線程\(Thread.current)正在執行\(i)號任務") } } group.notify(queue: DispatchQueue.main) { // 通知主線程,子線程操做已完成 print("全部任務都已經完成") }
在Swift4.0 中使用DispatchWorkItem代替原來OC中的dispatch_block_t。 在DispatchQueue執行操做,除了直接傳了一個() -> Void 類型的閉包外,還能夠傳入一個DispatchWorkItem任務對象。DispatchWorkItem的初始化方法能夠配置Qos和DispatchWorkItemFlags,可是這兩個參數都有默認參數,因此也能夠只傳入一個閉包。
DispatchWorkItemFlags枚舉中assignCurrentContext表示QoS根據建立時的context決定。 值得一提的是DispatchWorkItem也有wait方法,使用方式和DispatchGroup同樣。調用會等待這個workItem執行完。
let queue = DispatchQueue(label: "com.custom.thread", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent) let workItem = DispatchWorkItem { Thread.sleep(forTimeInterval: 2) print("線程\(Thread.current)正在執行任務") } queue.async(execute: workItem) print("before waiting") workItem.wait() print("after waiting")