【iOS】Swift4.0 GCD的使用筆記

https://www.jianshu.com/p/47e45367e524編程

前言

在Swift4.0版本中GCD的經常使用方法仍是有比較大的改動,這裏作個簡單的整理彙總。swift

GCD的隊列

隊列是一種遵循先進先出(FIFO)原則的數據結構,是一種特殊的線性表。安全

  主隊列 全局隊列 串行隊列 並行隊列
同步 X 並行同步 串行同步 並行同步
異步 串行異步 並行異步 串行異步 並行異步

X 表示禁止這麼使用,—— 表示不建議這麼使用。bash

1. 主隊列

主隊列默認是串行的,另外主隊列不能結合同步函數(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)") } } 
 
DF993F5D-6A1D-4869-A7E8-5E9D871915D8.png

2. 全局隊列

全局隊列是由系統建立的,默認是並行的。全局隊列具體運行在哪個線程,是由系統維護一個線程池,而後挑選其中的一至多條線程來使用。哪條線程會被使用是未知的,是由系統根據當前的併發任務,處理器的負載等狀況來決定的。閉包

  • 全局併發同步隊列
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)號任務") } } } 
 
9C11F7B3-7602-4E3B-94A2-0255DFC77077.png

從終端輸出咱們能夠知道任務被順序執行了,這是由於雖然當前是一個併發隊列,可是是同步執行的。同步操做會使得在前一個任務完成後纔去執行下一個任務。同步與異步的區別還在於它不會建立新的線程,而是直接在當前線程中執行了相關的任務,當前線程是主線程。同步會阻塞當前線程。併發

  • 全局併發異步隊列
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)號任務") } } } 
 
7D63F60B-3DC4-4071-854E-CCB3E36019E4.png

從終端輸出咱們能夠知道任務被隨機執行了,並且被分配在多個子線程中執行的,這符合併發的本質。另外須要注意的是全局併發異步隊列,系統在挑選來執行任務的線程的時候,會挑選除了主線程以外的其餘線程。異步

3. 自定義隊列

除了上述隊列以外,咱們還可使用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)

參數說明:

  1. label 表示隊列標籤

  2. qos 表示設置隊列的優先級

    • .userInteractive 須要用戶交互的,優先級最高,和主線程同樣
    • .userInitiated 即將須要,用戶指望優先級,優先級高比較高
    • .default 默認優先級
    • .utility 須要執行一段時間後,再通知用戶,優先級低
    • *.background 後臺執行的,優先級比較低
    • *.unspecified 不指定優先級,最低
  3. attributes 表示隊列類型,默認爲串行隊列,設置爲.concurrent表示並行隊列。iOS 10.0以後 attributes 新增.initiallyInactive屬性表示當前隊列是不活躍的,它須要調用DispatchQueueactivate方法來執行任務。

  4. autoreleaseFrequency 表示自動釋放頻率,設置自動釋放機制。

    • .inherit 表示不肯定,以前默認的行爲也是如今的默認值
    • .workItem 表示爲每一個執行的項目建立和排除自動釋放池, 項目完成時清理臨時對象
    • .never 表示GCD不爲您管理自動釋放池
  • 同步串行隊列
    其實同步串行隊列,沒什麼意思的,不論是同步操做仍是串行操做都會致使任務被一個一個的執行。這個操做尤爲是在主線程執行的時候須要注意,避免形成線程的卡頓。
let queue = DispatchQueue(label: "com.custom.thread")
queue.sync {
   //同步串行隊列
}
  • 異步串行隊列
    由於是串行隊列,即便是異步執行的,任務也是按照順序依次執行的,可是在子線程中執行的。
let queue = DispatchQueue(label: "com.custom.thread")
queue.async {
   //異步串行隊列
}
 
9D774A76-4F71-49AE-903A-57741B89D1DE.png

根據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)號任務")
     }
}
 
BC7AAE0B-C3FA-4AEB-9593-72E02D4A105F.png
  • 異步並行隊列
    異步並行隊列就會在多個線程中,隨機執行任務。
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)號任務") } } 
 
EA277C08-A7FF-49B5-B26B-73521A51C5CF.png

4. 任務組(DispatchGroup)

若是咱們想監聽多個任務的執行狀況,那麼咱們須要將任務(異步、同步、串行、並行)都添加到任務組中,而後經過DispatchGroupnotify函數就能夠監聽是否組內任務都已經完成。

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("全部任務都已經完成") } 
 
05A6287E-81AC-49A0-A277-D53994DB8E0A.png

5. 任務對象(DispatchWorkItem)

在Swift4.0 中使用DispatchWorkItem代替原來OC中的dispatch_block_t。 在DispatchQueue執行操做,除了直接傳了一個() -> Void 類型的閉包外,還能夠傳入一個DispatchWorkItem任務對象。DispatchWorkItem的初始化方法能夠配置QosDispatchWorkItemFlags,可是這兩個參數都有默認參數,因此也能夠只傳入一個閉包。
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")
 
A25890FA-C5B3-4000-94D1-D00044663A1A.png
 
 

簡介

Grand Central Dispatch (GCD) 是Apple開發的一個多核編程的較新的解決方法。它主要用於優化應用程序以支持多核處理器以及其餘對稱多處理系統。

衆所周知,GCD, NSOperationQueue, NSThread, pthread是iOS中多線程的幾種處理方式,Swift3以前GCD還是面向過程的寫法,因此須要封裝一層再使用。Swift3蘋果打成Dispatch這個module.你能夠經過import進行導入再使用。Swift4,直接使用。

特性

GCD可用於多核的並行運算
GCD會自動利用更多的CPU內核(好比雙核、四核)
GCD會自動管理線程的生命週期(建立線程、調度任務、銷燬線程)

用法

  1. 異步執行回主線程寫法
DispatchQueue.global().async { print("async do something\(Thread.current)") DispatchQueue.main.async { print("come back to main thread\(Thread.current)") } } 
  1. QoS
    以前接觸過Quality of Service仍是在VoIP,經過QoS來標註每一個通訊的priority,因此這邊實際上是把
DISPATCH_QUEUE_PRIORITY_HIGHT
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND

轉換成了

User Interactive 和用戶交互相關,好比動畫等等優先級最高。好比用戶連續拖拽的計算

User Initiated 須要馬上的結果,好比push一個ViewController以前的數據計算

Utility 能夠執行很長時間,再通知用戶結果。好比下載一個文件,給用戶下載進度

Background 用戶不可見,好比在後臺存儲大量數據

在GCD中,指定QoS有如下兩種方式
方式一,建立一個指定QoS的queue

let queue = DispatchQueue(label: "labelname", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit)

方式二,在提交block的時候,指定QoS

queue.async(group: nil, qos: .background, flags: .inheritQoS) { <#code#> } 

flags的參數有

public static let barrier: DispatchWorkItemFlags public static let detached: DispatchWorkItemFlags public static let assignCurrentContext: DispatchWorkItemFlags public static let noQoS: DispatchWorkItemFlags public static let inheritQoS: DispatchWorkItemFlags public static let enforceQoS: DispatchWorkItemFlags 

其中關於QoS的關係,能夠經過flags參數設置。

  1. DispatchWorkItem
let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .barrier) { <#code#> } 
  1. after
let deadline = DispatchTime.now() + 5.0 DispatchQueue.global().asyncAfter(deadline: deadline) { <#code#> } 
  1. DispatchGroup

DispatchGroup用來管理一組任務的執行,而後監放任務都完成的事件。好比,多個網絡請求同時發出去,等網絡請求都完成後reload UI。

let group = DispatchGroup() group.enter() self.sendHTTPRequest1(params:[String: Any]) { print("request complete") group.leave() } group.enter() self.sendHTTPRequest1(params:[String: Any]) { print("request complete") group.leave() } group.notify(queue: DispatchQueue.main) { print("all requests come back") } 
  1. Semaphore

Semaphore是保證線程安全的一種方式,並且繼OSSpinLock再也不安全後,Semaphore彷佛成爲了最快的加鎖的方式。
如圖


 
1513585102364922.png
let semaphore = DispatchSemaphore(value: 2) let queue = DispatchQueue.global() queue.async { semaphore.wait() self.sendHTTPRequest1(params:[String: Any]) { print("request complete") semaphore.signal() } } queue.async { semaphore.wait() self.sendHTTPRequest2(params:[String: Any]) { print("request complete") semaphore.signal() } } queue.async { semaphore.wait() self.sendHTTPRequest3(params:[String: Any]) { print("request complete") semaphore.signal() } } 
  1. Barrier

GCD裏的Barrier和NSOperationQueue的dependency比較接近,C任務開始以前須要A任務完成,或者A和B任務完成。

let queue = DispatchQueue(label: "foo", attributes: .concurrent) queue.async { self.sendHTTPRequest1(params:[String: Any]) { print("A") } } queue.async { self.sendHTTPRequest2(params:[String: Any]) { print("B") } } queue.async(flags: .barrier) { self.sendHTTPRequest3(params:[String: Any]) { print("C") } }
相關文章
相關標籤/搜索