dispatch_async與dispatch_sync區(qū)別

概述

首先明確幾個(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í)行的

參考

  1. GCD有關(guān)問題:dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"Hello ?");}); 死鎖的原因
  2. Objective-C高級(jí)編程 P161 dispatch_sync
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容