一早像、概念
隊(duì)列(DispatchQueue)
Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. Work submitted to dispatch queues executes on a pool of threads managed by the system. Except for the dispatch queue representing your app's main thread, the system makes no guarantees about which thread it uses to execute a task.
調(diào)度隊(duì)列是一個(gè)FIFO(先進(jìn)先出)隊(duì)列,應(yīng)用程序可以以block對(duì)象的形式向其提交任務(wù)。dispatch隊(duì)列可以串行或并發(fā)執(zhí)行任務(wù)橘洞。提交到調(diào)度隊(duì)列的任務(wù)執(zhí)行在系統(tǒng)管理的線程池上绍载。除了代表應(yīng)用程序主線程的調(diào)度隊(duì)列外割按,系統(tǒng)不保證是哪條線程執(zhí)行任務(wù)
由上面看出隊(duì)列有串行、并行兩種此蜈,那么他們有何區(qū)別呢?
串行隊(duì)列(Serial Dispatch Queue)
每次只執(zhí)行一個(gè)任務(wù)噪生。任務(wù)是按照進(jìn)入隊(duì)列的順序執(zhí)行的裆赵,并且只會(huì)開啟一條線程
并發(fā)隊(duì)列(Concurrent Dispatch Queue)
多個(gè)任務(wù)并發(fā)執(zhí)行,且可以開啟多條線程
任務(wù)
任務(wù)就是我們?cè)诰€程中執(zhí)行的那段代碼跺嗽,分為同步任務(wù)(sync)和異步任務(wù)(async)战授。其主要區(qū)別是:是否等待任務(wù)執(zhí)行完,以及是否具備開啟新線程的能力
同步任務(wù)(sync)
同步任務(wù)添加到指定的隊(duì)列中桨嫁,在它未執(zhí)行完成之前植兰,會(huì)一直等待,直到任務(wù)完成后才會(huì)返回璃吧;只能在當(dāng)前線程中執(zhí)行任務(wù)楣导,不具備開啟新線程的能力
異步任務(wù)(async)
異步任務(wù)添加到指定的隊(duì)列中,它不會(huì)等待任務(wù)完成畜挨,就會(huì)返回筒繁∝迹可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
1.串行隊(duì)列同步任務(wù)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 創(chuàng)建串行隊(duì)列
let queue = DispatchQueue(label: "myQueue", qos: .default)
queue.sync {
Thread.sleep(forTimeInterval: 1)
print("run task1 thread \(Thread.current)")
}
queue.sync {
Thread.sleep(forTimeInterval: 0.5)
print("run task2 thread \(Thread.current)")
}
}
輸出:
run task1 thread <NSThread: 0x6000012059c0>{number = 1, name = main}
run task2 thread <NSThread: 0x6000012059c0>{number = 1, name = main}
2.串行隊(duì)列異步任務(wù)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 創(chuàng)建串行隊(duì)列
let queue = DispatchQueue(label: "myQueue", qos: .default)
queue.async {
Thread.sleep(forTimeInterval: 1)
print("run task1 thread \(Thread.current)")
}
queue.async {
Thread.sleep(forTimeInterval: 0.5)
print("run task2 thread \(Thread.current)")
}
}
輸出:
run task1 thread <NSThread: 0x600001b33dc0>{number = 3, name = (null)}
run task2 thread <NSThread: 0x600001b33dc0>{number = 3, name = (null)}
3.并發(fā)隊(duì)列同步任務(wù)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 全局并行隊(duì)列
let queue = DispatchQueue.global()
queue.sync {
Thread.sleep(forTimeInterval: 1)
print("run task1 thread \(Thread.current)")
}
queue.sync {
Thread.sleep(forTimeInterval: 0.5)
print("run task2 thread \(Thread.current)")
}
queue.sync {
print("run task3 thread \(Thread.current)")
}
}
輸出:
run task1 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}
run task2 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}
run task3 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}
4.并發(fā)隊(duì)列異步任務(wù)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue.global()
queue.async {
print("run task1 thread \(Thread.current)")
}
queue.async {
print("run task2 thread \(Thread.current)")
}
queue.async {
print("run task3 thread \(Thread.current)")
}
}
輸出:
run task3 thread <NSThread: 0x600003589100>{number = 3, name = (null)}
run task2 thread <NSThread: 0x600003589140>{number = 4, name = (null)}
run task1 thread <NSThread: 0x6000035b76c0>{number = 5, name = (null)}
GCD死鎖
GCD死鎖就是毡咏,兩個(gè)任務(wù)相互等待驮宴,從而造成死鎖
死鎖原因??jī)蓚€(gè)任務(wù)相互等待
例:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("start")
DispatchQueue.main.sync { // 死鎖
print("ahaha")
}
print("end")
}
輸出:
start
怎么理解血当?
我們先看下面的代碼
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("Hello, World!")
run()
print("game over")
}
func run() {
print("do func run")
}
輸出:
Hello, World!
do func run
game over
我們?cè)趯W(xué)校的時(shí)候老師告訴我們代碼是按順序執(zhí)行幻赚,所以很好理解上面的輸出,即執(zhí)行完打印hello world臊旭,就執(zhí)行run函數(shù)落恼,當(dāng)執(zhí)行完run后再打印game over。再次印證了代碼是一步一步執(zhí)行的
离熏,很好請(qǐng)記住這句佳谦,很重要。
回到死鎖的例子來分析原因滋戳。首先執(zhí)行start钻蔑,這毫無疑問;然后執(zhí)行同步任務(wù)奸鸯,同步任務(wù)的特點(diǎn)是執(zhí)行完同步任務(wù)咪笑,再返回,意思是要等這個(gè)任務(wù)執(zhí)行完才會(huì)執(zhí)行end娄涩;然后我們來看下這個(gè)任務(wù)是加在哪個(gè)隊(duì)列窗怒?一看是DispatchQueue.main即主隊(duì)列,而當(dāng)前主隊(duì)列是在執(zhí)行viewDidLoad蓄拣,也就是說這個(gè)同步任務(wù)是加在viewDidLoad之后扬虚。因?yàn)橹麝?duì)列是一個(gè)串行隊(duì)列,也就是說球恤,隊(duì)列里的任務(wù)是一個(gè)接著一個(gè)執(zhí)行辜昵,只有前一個(gè)任務(wù)執(zhí)行完,才會(huì)執(zhí)行下一個(gè)咽斧。那么問題就出現(xiàn)了代碼執(zhí)行到DispatchQueue.main.sync這里時(shí)堪置,主隊(duì)列阻塞了,要等到任務(wù)執(zhí)行完张惹,但是主隊(duì)列的任務(wù)即viewDidLoad需要等到該任務(wù)執(zhí)行完才會(huì)執(zhí)行下一個(gè)任務(wù)舀锨,這就造成了兩個(gè)任務(wù)互相等待,從而死鎖
而下面這個(gè)例子將再次印證代碼是一步一步執(zhí)行的
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("start")
DispatchQueue.global().sync { // 不死鎖
print("ahaha")
}
print("end")
}
輸出:
start
ahaha
end
這為什么沒有死鎖呢诵叁?因?yàn)檫@個(gè)同步任務(wù)是加在全局并發(fā)隊(duì)列里雁竞,不是加在主隊(duì)列,因此當(dāng)全局并發(fā)隊(duì)列執(zhí)行完這個(gè)任務(wù),就會(huì)執(zhí)行end
二碑诉、GCD相關(guān)函數(shù)
2.1GCD柵欄方法
執(zhí)行隊(duì)列中在barrier之前加入的任務(wù)彪腔,才會(huì)執(zhí)行在barrier之后加入的任務(wù)。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue(label: "hahah", qos: .default, attributes: .concurrent)
queue.async {
print("begin task1..... will sleep 2 second")
Thread.sleep(forTimeInterval: 2)
print("end task1")
}
queue.async {
print("begin task2.....will sleep 3 second")
Thread.sleep(forTimeInterval: 3)
print("end task2")
}
let item = DispatchWorkItem(qos: .default, flags: .barrier) {
print("begin barrier sleep 2 second")
Thread.sleep(forTimeInterval: 2)
print("end barrier")
}
queue.async(execute: item)
queue.async {
print("task3....")
}
}
//輸出:
begin task1..... will sleep 2 second
begin task2.....will sleep 3 second
end task1
end task2
begin barrier sleep 2 second
end barrier
task3....
2.2 group notify
group.notify當(dāng)group中的任務(wù)執(zhí)行完之后才會(huì)執(zhí)行并且不會(huì)阻塞隊(duì)列
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue.global()
let group = DispatchGroup()
let download = DispatchWorkItem {
print("task1...")
}
queue.async(group: group, execute: download)
let saveDb = DispatchWorkItem {
print("task2...")
}
queue.async(group: group, execute: saveDb)
group.notify(queue: DispatchQueue.main) {
print("all finish")
}
print("end")
}
//輸出:
task2...
end
task1...
all finish
如果task是異步任務(wù)进栽,那么notify的任務(wù)就不會(huì)等到task1德挣、task2完成,那要怎么做了快毛,使用enter格嗅、level,注意enter唠帝、level需要成對(duì)出現(xiàn)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue.global()
let group = DispatchGroup()
group.enter()
let download = DispatchWorkItem {
queue.async {
Thread.sleep(forTimeInterval: 2)
print("download task1 fininsh")
group.leave()
}
}
queue.async(group: group, execute: download)
group.enter()
let saveDb = DispatchWorkItem {
queue.async {
Thread.sleep(forTimeInterval: 1.5)
print("download task2 finish")
group.leave()
}
}
queue.async(group: group, execute: saveDb)
group.notify(queue: DispatchQueue.main) {
print("all finish")
}
}
// 輸出
download task2 finish
download task1 fininsh
all finish
2.3信號(hào)量
使用DispatchSemaphore創(chuàng)建信號(hào)量
調(diào)用single()會(huì)使信號(hào)量+1
調(diào)用wait()會(huì)使信號(hào)量-1屯掖,當(dāng)信號(hào)量小于0時(shí)阻塞當(dāng)前線程
因此可以使用信號(hào)量可以讓多個(gè)異步任務(wù)達(dá)到同步執(zhí)行的效果
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global().async {
print("run task1")
Thread.sleep(forTimeInterval: 2)
semaphore.signal()
}
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
DispatchQueue.global().async {
print("run task2")
Thread.sleep(forTimeInterval: 1)
semaphore.signal()
}
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
DispatchQueue.global().async {
print("run task3")
}
}
//輸出:
run task1
run task2
run task3
2.4延遲執(zhí)行
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let queue = DispatchQueue.global()
let time = DispatchTime.now() + 3
queue.asyncAfter(deadline: time) {
print("after 3 sencond")
}
}
上面的3也可以使用DispatchTimeInterval的second、milliseconds襟衰、microseconds贴铜、nanoseconds來代替