原文連接前端
這篇文主要想總結下多線程在swift中的使用,先看下基本概念swift
進程指在系統中能獨立運行並做爲資源分配的基本單位,它是由一組機器指令、數據和堆棧等組成的,是一個能獨立運行的活動實體後端
線程是進程的基本執行單元,一個進程(程序)的全部任務都在線程中執行。數組
隊列,又稱爲佇列(queue),是先進先出(FIFO, First-In-First-Out)的線性表。在具體應用中一般用鏈表或者數組來實現。隊列只容許在後端(稱爲rear)進行插入操做,在前端(稱爲front)進行刪除操做。隊列的操做方式和堆棧相似,惟一的區別在於隊列只容許新數據在後端進行添加。安全
能夠這麼理解:(知乎)多線程
只要你是個正常人 , 都會選擇第二種 , 固然也有特殊狀況 , 那我的喜歡用熱水刷牙併發
指兩個或多個事件在同一時間間隔內發生。能夠在某條線程和其餘線程之間反覆屢次進行上下文切換,看上去就好像一個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")
複製代碼
初始化一個隊列最簡單的方式就是聲明它的標籤 。
打開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添加的任務會默認加入由系統管理的線程池,異步執行 。
當多個隊列同時執行的時候,系統須要知道哪一個隊列優先級更高,才能優先安排計算資源給他,咱們能夠這樣定義優先級:
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 }
}
複製代碼
在選擇優先級時能夠參考以下判斷 。
默認狀況下添加進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的形式包裹起來,在不一樣的線程中執行 。
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)")
})
複製代碼
DispatchGroup 能夠用來管理一組隊列,監聽全部隊列的全部任務都完成的通知,比較經常使用的就是在一個頁面請求多個接口的時候,所有請求完再刷新UI 。
總之,使用GCD一方面會提高咱們應用的性能,給用戶帶來更好的體驗,不過必定要注意線程安全問題。