Swift3.0 GCD多執行緒詳解
GCD思維導圖
GCD(Grand Central Dispatch)是蘋果公司實現的一套先進先出執行的任務佇列, 我們只要將程式閉包傳給GCD, mac os會在系統執行緒池裡執行該任務, 而且無法確定到底是哪個執行緒執行的。 總之, GCD是個順序或併發執行佇列, 封裝了執行緒的排程, 開發者不用直接操作執行緒了。
DispatchQueue支援同步sync和非同步async方法,每個人物即DispatchWorkItem只執行一遍; 同步和併發方法都是按照先進先出的順序執行佇列裡的任務。
App程序在啟動時系統會自動建立一個main queue即DispatchQueue.main, 注意該queue裡不能執行耗時操作並只能在main佇列裡重新整理介面。
1、DispatchQueue基本用法非同步async:在子執行緒執行耗時操作完成後,將結果重新整理到介面; 注意放開註釋使用例項化queue和global的效果是一樣的。
這段程式碼的作用從網上下載一張圖片並顯示在UIImageView, 註釋值傳遞! 根據閉包的語法特性, 閉包內可以訪問外部的變數, 例如DispatchQueue.main.async閉包內能夠使用外部變數self。//let queue = DispatchQueue(label: "com.brycegao.gcdtest") DispatchQueue.global().async { //queue.async { let url = URL(string: "http://img-arch.pconline.com.cn/images/upload/upc/tx/photoblog/1107/05/c5/8235345_8235345_1309860279806.jpg")! if let imageData = try? Data(contentsOf: url) { //從網上取資料,屬於耗時操作 let tmpimage = UIImage(data: imageData as Data) //二進位制資料轉換為圖片,屬於耗時操作 DispatchQueue.main.async { //通知ui重新整理 self.image = tmpimage self.imageView.image = self.image } } }
從日誌可以看出FIFO的特點,先新增的任務肯定先執行let queue = DispatchQueue(label: "com.brycegao.gcdtest") queue.async { let date = Date() print("async1 \(date.description)") Thread.sleep(forTimeInterval: 1) //停止1秒 } queue.async { let date = Date() print("async2 \(date.description)") Thread.sleep(forTimeInterval: 1) } queue.async { let date = Date() print("async3 \(date.description)") Thread.sleep(forTimeInterval: 1) }
async1 2016-12-27 13:47:38 +0000
async2 2016-12-27 13:47:39 +0000
async3 2016-12-27 13:47:40 +0000
2、DispatchQueue基本用法同步sync, 該方法會阻塞UI佇列, 導致不顯示控制元件或無點選事件等問題; sync方法仍然按照FIFO順序執行。
let queue = DispatchQueue(label: "com.brycegao.gcdtest")
queue.async { //非同步方法不阻塞UI
let date = Date()
print("async1 \(date.description)")
Thread.sleep(forTimeInterval: 1) //停止1秒
}
queue.sync { //同步方法會阻塞UI,造成不顯示控制元件或無點選事件,但仍然是順序執行
let date = Date()
print("sync \(date.description)")
Thread.sleep(forTimeInterval: 10)
}
queue.async {
let date = Date()
print("async3 \(date.description)")
Thread.sleep(forTimeInterval: 1)
}
async1 2016-12-27 13:55:17 +0000
sync 2016-12-27 13:55:18 +0000
async3 2016-12-27 13:55:28 +0000
3、DispatchQos用於描述佇列優先順序, 從高到低分為userInteractive,userInitiated,default,utility,background, 預設是default。
4、上面介紹的是序列佇列(預設), 現在介紹並行佇列。 並行對列只能通過例項化方式得到, 區別是有.concurrent引數。將上面的示例程式碼稍作改動, 即修改DispatchQueue的例項化方法引數。
let conqueue = DispatchQueue(label: "queuename", attributes: .concurrent) //併發佇列
conqueue.async {
let date = Date()
print("async1 \(date.description)")
Thread.sleep(forTimeInterval: 1) //停止1秒
}
conqueue.async { //同步方法會阻塞UI,造成不顯示控制元件或無點選事件,但仍然是順序執行
let date = Date()
print("async2 \(date.description)")
Thread.sleep(forTimeInterval: 1)
}
conqueue.async {
let date = Date()
print("async3 \(date.description)")
Thread.sleep(forTimeInterval: 1)
} /*DispatchQueue.global().async
從日誌看跟普通的多執行緒併發是一樣的, 在這裡是在mac os執行緒池內執行的。
async1 2016-12-27 14:08:21 +0000
async2 2016-12-27 14:08:21 +0000
async3 2016-12-27 14:08:21 +0000
5、設定執行時間asyncAfter函式可以設定延遲一段時間後執行閉包,功能類似於定時器。 還是在上面示例程式碼上稍作修改。
.....
conqueue.async {
let date = Date()
print("async1 \(date.description)")
Thread.sleep(forTimeInterval: 1) //停止1秒
}
let time = DispatchTime.now() + 3
conqueue.asyncAfter(deadline: time, execute: {
let date = Date()
print("asyncAfter \(date.description)")
})
....
輸出:
async1 2016-12-27 14:16:11 +0000
async3 2016-12-27 14:16:11 +0000
async2 2016-12-27 14:16:11 +0000
asyncAfter 2016-12-27 14:16:14 +0000
6、DispatchGroup的作用就是監聽一個或多個DispatchQueue任務結束的觸發事件, 類似於Java的wait/notifyAll。
let group = DispatchGroup()
let queue1 = DispatchQueue(label: "queue1")
queue1.async(group: group) {
Thread.sleep(forTimeInterval: 1) //停止1秒
let date = Date()
print("async1 \(date.description)")
}
let queue2 = DispatchQueue(label: "queue2")
queue2.async(group: group) {
Thread.sleep(forTimeInterval: 3)
let date = Date()
print("asycn2 \(date.description)")
}
let queue3 = DispatchQueue(label: "queue3")
queue3.async(group: group){
Thread.sleep(forTimeInterval: 1)
let date = Date()
print("async3 \(date.description)")
}
let date1 = Date()
print("date1: \(date1.description)")
group.wait() //等待group的任務都執行完成後向下執行
let date2 = Date()
print("date2: \(date2.description)")
日誌:date1: 2016-12-27 14:32:52 +0000
async3 2016-12-27 14:32:53 +0000
async1 2016-12-27 14:32:53 +0000
asycn2 2016-12-27 14:32:55 +0000
date2: 2016-12-27 14:32:55 +0000
如上面示例程式碼, 將3個DispatchQueue的任務新增到DispatchGroup中, date1先打印出來, 等到async1、async2、async3都執行完成後才打印date2。