? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?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的效果是一樣的。
//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
}
}
}
這段代碼的作用從網上下載一張圖片并顯示在UIImageView, 注釋值傳遞! 根據閉包的語法特性, 閉包內可以訪問外部的變量, 例如DispatchQueue.main.async閉包內能夠使用外部變量self。
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)
}
從日志可以看出FIFO的特點,先添加的任務肯定先執行
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。