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