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