概述
首先明確幾個概念
- 隊列:隊列分為串行和并行。串行隊列按照A、B、C、D的順序添加四個任務,這四個任務按照順序執行,結束順序也肯定是A、B、C、D,而并行隊列同時執行這四個任務,完成的順序因此也是隨機的。
- 異步執行(async)和同步執行(sync):使用dispatch_async調用一個block,這個block會被放到指定的queue_1隊列尾等待執行,至于這個block是被并行還是串行執行,只和dispatch_async中的指定的queue_1有關,但是dispatch_async會馬上返回。使用dispatch_sync同樣也是把block放到指定的queue_2上執行,但是會等待這個block執行完畢后才返回,這期間會阻塞當前運行調用dispatch_async或dispatch_sync代碼的queue(通常為main_queue)直到sync函數返回。
以打電話給查號臺為例:
- 同步:打電話給查號臺,問某個地方的電話號碼,接線員會告訴你稍等,然后為你查號,此時你的電話沒有掛斷,其他的電話也不能打進來,等到接線員查找到了你要找的電話號,告訴你后,才將電話掛斷
- 異步:打電話給查號臺,問某個地方的電話號碼,接線員知道了你的請求后,會立刻掛斷電話,此時其他的電話可以打進來。然后開始為你查號。等到查找到了你要找的電話號,會再打電話通知你。
示例
異步
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"async:1");
});
NSLog(@"async:2");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"async:3");
});
NSLog(@"async:4");
結果為
async:2
async:4
async:1
async:3
可以看出,dispatch_async將block追加到線程中后,并未等待,立刻執行后面的代碼
同步
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"sync:1");
});
NSLog(@"sync:2");
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"sync:3");
});
NSLog(@"sync:4");
結果為
sync:1
sync:2
sync:3
sync:4
可以看出,dispatch_sync將block追加到線程中后,等待block執行完畢后才接著執行后面的代碼
死鎖
// 前提條件:當前的 queue 為 main_queue
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"mainQueue_sync:1");
});
上述代碼會造成死鎖。原因:前提條件是當前 queue 為 main_queue。main_queue 為串行隊列,在當前 queue 上調用 sync 函數。需要執行的 block 被放到當前 queue 的隊尾等待被執行,因為這是一個串行的 queue,調用 sync 函數會阻塞當前隊列,等待 block 被執行->這個 block 一直不會被執行-> sync 函數一直不返回,所以當前 queue 就被阻塞了,造成了死鎖。
一般串行隊列中 sync 到自身上會產生死鎖,sync 到其他隊列上一般不會產生死鎖,如在自定義 queue 中 sync main_queue,等到 main_queue 執行完畢再繼續執行操作。
說明
開發者要做的只是定義想執行的任務并追加到適當的Dispatch Queue中。
上述引用自蘋果官方對GCD的說明,因此Dispatch_async和Dispatch_sync的作用是將block追加到隊列中。這句話對于上述理解死鎖有很大幫助。
實例
print(1)
serialQueue.async {
print(2)
serialQueue.sync {
print(3)
}
print(4)
}
print(5)
分析:只會打印1、5、2,然后就死鎖了。原因是列serialQueue.async
的block1被異步追加到串行隊列上后,開始執行,這個block1中又被同步追加了一個block2,此時serialQueue被阻塞,等待block2執行完畢,但是block1還未執行完畢,由于是串行隊列,block只能按照追加的先后順序一個一個執行:線程被阻塞->block1停止執行->block2等block1執行完畢->因此就造成了死鎖。
注意
dispatch_sync 官方文檔
As an optimization, dispatch_sync() invokes the block on the current thread when possible.
作為優化,如果可能,直接在當前線程調用這個block。
通過dispatch_sync添加的任務,在哪個線程添加就會在哪個線程執行。因此向并發隊列添加的任務,沒有開啟新線程,而是在主線程執行的