GCD

我們知道在iOS開發(fā)中,一共有四種多線程技術(shù):pthread坟漱,NSThread竟纳,GCD,NSOperation:

前兩者是面向線程開發(fā)的多線程技術(shù)息罗,需要開發(fā)者自己去維護(hù)線程的生命周期掂咒,比較繁瑣。

后兩者是面向隊(duì)列開發(fā)的多線程技術(shù)迈喉,開發(fā)者僅僅定義想執(zhí)行的任務(wù)追加到適當(dāng)?shù)腄ispatch Queue(隊(duì)列)中并設(shè)置一些優(yōu)先級(jí)绍刮,依賴等操作就可以了,其他的事情可以交給系統(tǒng)來做挨摸。

GCD它是基于C語言的API孩革,開發(fā)者只需要將任務(wù)放在block內(nèi),并指定好追加的隊(duì)列,就可以完成多線程開發(fā)章喉。

但是多線程開發(fā)時(shí)容易發(fā)生的一些問題:

·多個(gè)線程更新相同的資源:數(shù)據(jù)競爭唬滑。

·多個(gè)線程相互持續(xù)等待:死鎖。

·使用太多的線程導(dǎo)致消耗內(nèi)存彬檀。

雖然解決這些問題的代價(jià)是會(huì)使程序的復(fù)雜度上升,但是多線程技術(shù)仍然是必須使用的:因?yàn)槭褂枚嗑€程編程可以保證應(yīng)用程序的響應(yīng)性能瞬女。如果耗時(shí)操作阻塞了主線程的RunLoop窍帝,會(huì)導(dǎo)致用戶界面無法響應(yīng)用戶的操作,所以必須開啟子線程將耗時(shí)操作放在子線程中處理诽偷。那么我們應(yīng)該怎么進(jìn)行多線程開發(fā)呢坤学?在講解之前先看一下本文結(jié)構(gòu)(GCD部分):

隊(duì)列

Dispatch Queue是執(zhí)行處理的等待隊(duì)列,按照任務(wù)(block)追加到隊(duì)列里的順序报慕,先進(jìn)先出執(zhí)行處理深浮。

而等待隊(duì)列有兩種

Serial Dispatch Queue:串行隊(duì)列,等待當(dāng)前執(zhí)行任務(wù)處理結(jié)束的隊(duì)列眠冈。

Concurrent Dispatch Queue:并發(fā)隊(duì)列飞苇,不等待當(dāng)前執(zhí)行任務(wù)處理結(jié)束的隊(duì)列。

串行隊(duì)列

將任務(wù)追加到串行隊(duì)列:

- (void)serialQueue{dispatch_queue_tqueue = dispatch_queue_create("serial queue",NULL);for(NSIntegerindex =0; index <6; index ++) {dispatch_async(queue, ^{NSLog(@"task index %ld in serial queue",index);? ? ? ? });? ? }}

輸出:

gcd_demo[33484:2481120] task index0inserial queuegcd_demo[33484:2481120] task index1inserial queuegcd_demo[33484:2481120] task index2inserial queuegcd_demo[33484:2481120] task index3inserial queuegcd_demo[33484:2481120] task index4inserial queuegcd_demo[33484:2481120] task index5inserial queue

通過dispatch_queue_create函數(shù)可以創(chuàng)建隊(duì)列蜗顽,第一個(gè)函數(shù)為隊(duì)列的名稱布卡,第二個(gè)參數(shù)是NULL和DISPATCH_QUEUE_SERIAL時(shí),返回的隊(duì)列就是串行隊(duì)列雇盖。

為了避免重復(fù)代碼忿等,我在這里使用了for循環(huán),將任務(wù)追加到了queue中崔挖。

注意贸街,這里的任務(wù)是按照順序執(zhí)行的庵寞。說明任務(wù)是以阻塞的形式執(zhí)行的:必須等待上一個(gè)任務(wù)執(zhí)行完成才能執(zhí)行現(xiàn)在的任務(wù)。也就是說:一個(gè)Serial Dispatch Queue中同時(shí)只能執(zhí)行一個(gè)追加處理(任務(wù)block)薛匪,而且系統(tǒng)對于一個(gè)Serial Dispatch Queue只生成并使用一個(gè)線程捐川。

但是,如果我們將6個(gè)任務(wù)分別追加到6個(gè)Serial Dispatch Queue中蛋辈,那么系統(tǒng)就會(huì)同時(shí)處理這6個(gè)任務(wù)(因?yàn)闀?huì)另開啟6個(gè)子線程):

- (void)multiSerialQueue{for(NSIntegerindex =0; index <10; index ++) {//新建一個(gè)serial queuedispatch_queue_tqueue = dispatch_queue_create("different serial queue",NULL);dispatch_async(queue, ^{NSLog(@"serial queue index : %ld",index);? ? ? ? });? ? }}

輸出結(jié)果:

gcd_demo[33576:2485282] serial queue index :1gcd_demo[33576:2485264] serial queue index :0gcd_demo[33576:2485267] serial queue index :2gcd_demo[33576:2485265] serial queue index :3gcd_demo[33576:2485291] serial queue index :4gcd_demo[33576:2485265] serial queue index :5

從輸出結(jié)果可以看出來属拾,這里的6個(gè)任務(wù)并不是按順序執(zhí)行的。

需要注意的是:一旦開發(fā)者新建了一個(gè)串行隊(duì)列冷溶,系統(tǒng)一定會(huì)開啟一個(gè)子線程渐白,所以在使用串行隊(duì)列的時(shí)候,一定只創(chuàng)建真正需要?jiǎng)?chuàng)建的串行隊(duì)列逞频,避免資源浪費(fèi)纯衍。

并發(fā)隊(duì)列

將任務(wù)追加到并發(fā)隊(duì)列:

- (void)concurrentQueue{dispatch_queue_tqueue = dispatch_queue_create("concurrent queue", DISPATCH_QUEUE_CONCURRENT);for(NSIntegerindex =0; index <6; index ++) {dispatch_async(queue, ^{NSLog(@"task index %ld in concurrent queue",index);? ? ? ? });? ? }}

輸出結(jié)果:

gcd_demo[33550:2484160] task index1inconcurrent queuegcd_demo[33550:2484159] task index0inconcurrent queuegcd_demo[33550:2484162] task index2inconcurrent queuegcd_demo[33550:2484182] task index3inconcurrent queuegcd_demo[33550:2484183] task index4inconcurrent queuegcd_demo[33550:2484160] task index5inconcurrent queue

可以看到,dispatch_queue_create函數(shù)的第二個(gè)參數(shù)是DISPATCH_QUEUE_CONCURRENT苗胀。

注意襟诸,這里追加到并發(fā)隊(duì)列的6個(gè)任務(wù)并不是按照順序執(zhí)行的,符合上面并發(fā)隊(duì)列的定義基协。

擴(kuò)展知識(shí):iOS和OSX基于Dispatch Queue中的處理數(shù)歌亲,CPU核數(shù),以及CPU負(fù)荷等當(dāng)前系統(tǒng)的狀態(tài)來決定Concurrent Dispatch Queue中并發(fā)處理的任務(wù)數(shù)澜驮。

隊(duì)列的命名

現(xiàn)在我們知道dispatch_queue_create方法第一個(gè)參數(shù)指定了這個(gè)新建隊(duì)列的名稱陷揪,推薦使用逆序quan cheng全程域名(FQDN,fully qualified domain name)。這個(gè)名稱可以在Xcode和CrashLog中顯示出來杂穷,對bug的追蹤很有幫助悍缠。

在繼續(xù)講解之前做個(gè)小總結(jié),現(xiàn)在我們知道了:

·如何創(chuàng)建串行隊(duì)列和并發(fā)隊(duì)列耐量。

·將任務(wù)追加到這兩種隊(duì)列里以后的執(zhí)行效果飞蚓。

·將任務(wù)追加到多個(gè)串行隊(duì)列會(huì)使這幾個(gè)任務(wù)在不同的線程執(zhí)行。

實(shí)際上廊蜒,系統(tǒng)給我們提供了兩種特殊的隊(duì)列趴拧,分別對應(yīng)串行隊(duì)列和并發(fā)隊(duì)列:

系統(tǒng)提供的隊(duì)列

Main Dispatch Queue

主隊(duì)列:放在這個(gè)隊(duì)列里的任務(wù)會(huì)追加到主線程的RunLoop中執(zhí)行。需要刷新UI的時(shí)候我們可以直接獲取這個(gè)隊(duì)列劲藐,將任務(wù)追加到這個(gè)隊(duì)列中八堡。

Globle Dispatch Queue

全局并發(fā)隊(duì)列:開發(fā)者可以不需要特意通過dispatch_queue_create方法創(chuàng)建一個(gè)Concurrent Dispatch Queue,可以將任務(wù)直接放在這個(gè)全局并發(fā)隊(duì)列里面聘芜。

有一個(gè)常見的例子可以充分體現(xiàn)二者的使用方法:

//獲取全局并發(fā)隊(duì)列進(jìn)行耗時(shí)操作dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{//加載圖片NSData*dataFromURL = [NSDatadataWithContentsOfURL:imageURL];UIImage*imageFromData = [UIImageimageWithData:dataFromURL];dispatch_async(dispatch_get_main_queue(), ^{//獲取主隊(duì)列,在圖片加載完成后更新UIImageViewUIImageView*imageView = [[UIImageViewalloc] initWithImage:imageFromData];? ? ? ? ? ? ? ? });? ? ? ? });

GCD的各種函數(shù)

dispatch_set_target_queue

這個(gè)函數(shù)有兩個(gè)作用:

·改變隊(duì)列的優(yōu)先級(jí)缝龄。

·防止多個(gè)串行隊(duì)列的并發(fā)執(zhí)行汰现。

改變隊(duì)列的優(yōu)先級(jí)

dispatch_queue_create方法生成的串行隊(duì)列合并發(fā)隊(duì)列的優(yōu)先級(jí)都是與默認(rèn)優(yōu)先級(jí)的Globle Dispatch Queue一致挂谍。

如果想要變更某個(gè)隊(duì)列的優(yōu)先級(jí),需要使用dispatch_set_target_queue函數(shù)瞎饲。

舉個(gè)??:創(chuàng)建一個(gè)在后臺(tái)執(zhí)行動(dòng)作處理的Serial Dispatch Queue

//需求:生成一個(gè)后臺(tái)的串行隊(duì)列- (void)changePriority{dispatch_queue_tqueue = dispatch_queue_create("queue",NULL);dispatch_queue_tbgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);//第一個(gè)參數(shù):需要改變優(yōu)先級(jí)的隊(duì)列口叙;//第二個(gè)參數(shù):目標(biāo)隊(duì)列dispatch_set_target_queue(queue, bgQueue);}

防止多個(gè)串行隊(duì)列的并發(fā)執(zhí)行

有時(shí),我們將不能并發(fā)執(zhí)行的處理追加到多個(gè)Serial Dispatch Queue中時(shí)嗅战,可以使用dispatch_set_target_queue函數(shù)將目標(biāo)函數(shù)定為某個(gè)Serial Dispatch Queue妄田,就可以防止這些處理的并發(fā)執(zhí)行。

代碼:

NSMutableArray*array = [NSMutableArrayarray];for(NSIntegerindex =0; index <5; index ++) {//5個(gè)串行隊(duì)列dispatch_queue_tserial_queue = dispatch_queue_create("serial_queue",NULL);? ? ? ? [array addObject:serial_queue];}[array enumerateObjectsUsingBlock:^(dispatch_queue_tqueue,NSUIntegeridx,BOOL* _Nonnull stop) {dispatch_async(queue, ^{NSLog(@"任務(wù)%ld",idx);? ? });}];

輸出:

gcd_demo[40329:2999714] 任務(wù)1gcd_demo[40329:2999726] 任務(wù)0gcd_demo[40329:2999717] 任務(wù)2gcd_demo[40329:2999715] 任務(wù)3gcd_demo[40329:2999730] 任務(wù)4

我們可以看到驮捍,如果僅僅是將任務(wù)追加到5個(gè)串行隊(duì)列中疟呐,那么這些任務(wù)就會(huì)并發(fā)執(zhí)行。

那接下來看看使用dispatch_set_target_queue方法以后:

//多個(gè)串行隊(duì)列东且,設(shè)置了target queueNSMutableArray*array = [NSMutableArrayarray];dispatch_queue_tserial_queue_target = dispatch_queue_create("queue_target",NULL);for(NSIntegerindex =0; index <5; index ++) {//分別給每個(gè)隊(duì)列設(shè)置相同的target queuedispatch_queue_tserial_queue = dispatch_queue_create("serial_queue",NULL);? ? dispatch_set_target_queue(serial_queue, serial_queue_target);? ? [array addObject:serial_queue];}[array enumerateObjectsUsingBlock:^(dispatch_queue_tqueue,NSUIntegeridx,BOOL* _Nonnull stop) {dispatch_async(queue, ^{NSLog(@"任務(wù)%ld",idx);? ? });}];

輸出:

gcd_demo[40408:3004382] 任務(wù)0gcd_demo[40408:3004382] 任務(wù)1gcd_demo[40408:3004382] 任務(wù)2gcd_demo[40408:3004382] 任務(wù)3gcd_demo[40408:3004382] 任務(wù)4

很顯然启具,這些任務(wù)就按順序執(zhí)行了。

dispatch_after

dispatch_after解決的問題:某個(gè)線程里珊泳,在指定的時(shí)間后處理某個(gè)任務(wù):

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"三秒之后追加到隊(duì)列");});

注意:不是在3秒之后處理任務(wù)鲁冯,準(zhǔn)確來說是3秒之后追加到隊(duì)列。所以說色查,如果這個(gè)線程的runloop執(zhí)行1/60秒一次薯演,那么這個(gè)block最快會(huì)在3秒后執(zhí)行,最慢會(huì)在(3+1/60)秒后執(zhí)行秧了。而且跨扮,如果這個(gè)隊(duì)列本身還有延遲,那么這個(gè)block的延遲執(zhí)行時(shí)間會(huì)更多示惊。

dispatch_group

·如果遇到這樣到需求:全部處理完多個(gè)預(yù)處理任務(wù)(block_1 ~ 4)后執(zhí)行某個(gè)任務(wù)(block_finish)好港,我們有兩個(gè)方法:

·如果預(yù)處理任務(wù)需要一個(gè)接一個(gè)的執(zhí)行:將所有需要先處理完的任務(wù)追加到Serial Dispatch Queue中,并在最后追加最后處理的任務(wù)(block_finish)米罚。

·如果預(yù)處理任務(wù)需要并發(fā)執(zhí)行:需要使用dispatch_group函數(shù)钧汹,將這些預(yù)處理的block追加到global dispatch queue中。

分別詳細(xì)講解一下兩種需求的實(shí)現(xiàn)方式:

預(yù)處理任務(wù)需要一個(gè)接一個(gè)的執(zhí)行:

這個(gè)需求的實(shí)現(xiàn)方式相對簡單一點(diǎn)录择,只要將所有的任務(wù)(block_1 ~ 4 + block_finish)放在一個(gè)串行隊(duì)列中即可拔莱,因?yàn)槎际前凑枕樞驁?zhí)行的,只要不做多余的事情隘竭,這些任務(wù)就會(huì)乖乖地按順序執(zhí)行塘秦。

預(yù)處理任務(wù)需要一個(gè)接一個(gè)的執(zhí)行:

dispatch_group_t group = dispatch_group_create();dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);for(NSIntegerindex =0; index <5; index ++) {? ? ? ? dispatch_group_async(group, queue, ^{NSLog(@"任務(wù)%ld",index);? ? ? ? });}dispatch_group_notify(group, queue, ^{NSLog(@"最后的任務(wù)");});

輸出:

gcd_demo[40905:3057237] 任務(wù)0gcd_demo[40905:3057235] 任務(wù)1gcd_demo[40905:3057234] 任務(wù)2gcd_demo[40905:3057253] 任務(wù)3gcd_demo[40905:3057237] 任務(wù)4gcd_demo[40905:3057237] 最后的任務(wù)

因?yàn)檫@些預(yù)處理任務(wù)都是追加到global dispatch queue中的,所以這些任務(wù)的執(zhí)行任務(wù)的順序是不定的动看。但是最后的任務(wù)一定是最后輸出的尊剔。

dispatch_group_notify函數(shù)監(jiān)聽傳入的group中任務(wù)的完成,等這些任務(wù)全部執(zhí)行以后菱皆,再將第三個(gè)參數(shù)(block)追加到第二個(gè)參數(shù)的queue(相同的queue)中须误。

dispatch_group_wait

dispatch_group_wait 也是配合dispatch_group 使用的挨稿,利用這個(gè)函數(shù),我們可以設(shè)定group內(nèi)部所有任務(wù)執(zhí)行完成的超時(shí)時(shí)間京痢。

一共有兩種情況:超時(shí)的情況和沒有超時(shí)的情況:

超時(shí)的情況:

- (void)dispatch_wait_1{? ? dispatch_group_t group = dispatch_group_create();dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);for(NSIntegerindex =0; index <5; index ++) {? ? ? ? dispatch_group_async(group, queue, ^{for(NSIntegeri =0; i<1000000000; i ++) {? ? ? ? ? ? }NSLog(@"任務(wù)%ld",index);? ? ? ? });? ? }? ? dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,1ull *NSEC_PER_SEC);longresult = dispatch_group_wait(group, time);if(result ==0) {NSLog(@"group內(nèi)部的任務(wù)全部結(jié)束");? ? }else{NSLog(@"雖然過了超時(shí)時(shí)間奶甘,group還有任務(wù)沒有完成");? ? }}

輸出:

gcd_demo[41277:3087481] 雖然過了超時(shí)時(shí)間,group還有任務(wù)沒有完成祭椰,結(jié)果是判定為超時(shí)gcd_demo[41277:3087563] 任務(wù)0gcd_demo[41277:3087564] 任務(wù)2gcd_demo[41277:3087579] 任務(wù)3gcd_demo[41277:3087566] 任務(wù)1gcd_demo[41277:3087563] 任務(wù)4

沒有超時(shí)的情況:

- (void)dispatch_wait_2{? ? dispatch_group_t group = dispatch_group_create();dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);for(NSIntegerindex =0; index <5; index ++) {? ? ? ? dispatch_group_async(group, queue, ^{for(NSIntegeri =0; i<100000000; i ++) {? ? ? ? ? ? }NSLog(@"任務(wù)%ld",index);? ? ? ? });? ? }? ? dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,1ull *NSEC_PER_SEC);longresult = dispatch_group_wait(group, time);if(result ==0) {NSLog(@"group內(nèi)部的任務(wù)全部結(jié)束");? ? }else{NSLog(@"雖然過了超時(shí)時(shí)間臭家,group還有任務(wù)沒有完成");? ? }}

輸出:

gcd_demo[41357:3092079] 任務(wù)2gcd_demo[41357:3092076] 任務(wù)3gcd_demo[41357:3092092] 任務(wù)1gcd_demo[41357:3092077] 任務(wù)0gcd_demo[41357:3092079] 任務(wù)4gcd_demo[41357:3091956] group內(nèi)部的任務(wù)全部結(jié)束,在超時(shí)的時(shí)間以內(nèi)完成方淤,結(jié)果判定為沒有超時(shí)

注意:

一旦調(diào)用dispatch_group_wait以后钉赁,當(dāng)經(jīng)過了函數(shù)中指定的超時(shí)時(shí)間后 或者 指定的group內(nèi)的任務(wù)全部執(zhí)行后會(huì)返回這個(gè)函數(shù)的結(jié)果:

經(jīng)過了函數(shù)中指定的超時(shí)時(shí)間后,group內(nèi)部的任務(wù)沒有全部完成臣淤,判定為超時(shí)橄霉,否則,沒有超時(shí)

指定的group內(nèi)的任務(wù)全部執(zhí)行后邑蒋,經(jīng)過的時(shí)間長于超時(shí)時(shí)間姓蜂,判定為超時(shí),否則医吊,沒有超時(shí)钱慢。

也就是說:

如果指定的超時(shí)時(shí)間為DISPATCH_TIME_NOW,那么則沒有等待卿堂,立即判斷group內(nèi)的任務(wù)是否完成束莫。

可以看出,指定的超時(shí)時(shí)間為DISPATCH_TIME_NOW的時(shí)候相當(dāng)于dispatch_group_notify函數(shù)的使用:判斷group內(nèi)的任務(wù)是否都完成草描。

然而dispatch_group_notify函數(shù)是作者推薦的览绿,因?yàn)橥ㄟ^這個(gè)函數(shù)可以直接設(shè)置最后任務(wù)所被追加的隊(duì)列,使用起來相對比較方便穗慕。

dispatch_barrier_async

關(guān)于解決數(shù)據(jù)競爭的方法:讀取處理是可以并發(fā)的饿敲,但是寫入處理卻是不允許并發(fā)執(zhí)行的。

所以合理的方案是這樣的:

·讀取處理追加到concurrent dispatch queue中

·寫入處理在任何一個(gè)讀取處理沒有執(zhí)行的狀態(tài)下逛绵,追加到serial dispatch queue中(也就是說怀各,在寫入處理結(jié)束之前,讀取處理不可執(zhí)行)术浪。

我們看看如何使用dispatch_barrier_async來解決這個(gè)問題瓢对。

為了幫助大家理解,我構(gòu)思了一個(gè)例子:

·3名董事和總裁開會(huì)胰苏,在每個(gè)人都查看完合同之后硕蛹,由總裁簽字。

·總裁簽字之后,所有人再審核一次合同妓美。

這個(gè)需求有三個(gè)關(guān)鍵點(diǎn):

·關(guān)鍵點(diǎn)1:所有與會(huì)人員查看和審核合同僵腺,是同時(shí)進(jìn)行的鲤孵,無序的行為壶栋。

·關(guān)鍵點(diǎn)2:只有與會(huì)人員都查看了合同之后,總裁才能簽字普监。

·關(guān)鍵點(diǎn)3: 只有總裁簽字之后贵试,才能進(jìn)行審核。

用代碼看一下:

- (void)dispatch_barrier{dispatch_queue_tmeetingQueue = dispatch_queue_create("com.meeting.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(meetingQueue, ^{NSLog(@"總裁查看合同");? ? });dispatch_async(meetingQueue, ^{NSLog(@"董事1查看合同");? ? });dispatch_async(meetingQueue, ^{NSLog(@"董事2查看合同");? ? });dispatch_async(meetingQueue, ^{NSLog(@"董事3查看合同");? ? });? ? dispatch_barrier_async(meetingQueue, ^{NSLog(@"總裁簽字");? ? });dispatch_async(meetingQueue, ^{NSLog(@"總裁審核合同");? ? });dispatch_async(meetingQueue, ^{NSLog(@"董事1審核合同");? ? });dispatch_async(meetingQueue, ^{NSLog(@"董事2審核合同");? ? });dispatch_async(meetingQueue, ^{NSLog(@"董事3審核合同");? ? });}

輸出結(jié)果:

gcd_demo[41791:3140315] 總裁查看合同gcd_demo[41791:3140296] 董事1查看合同gcd_demo[41791:3140297] 董事3查看合同gcd_demo[41791:3140299] 董事2查看合同gcd_demo[41791:3140299] 總裁簽字gcd_demo[41791:3140299] 總裁審核合同gcd_demo[41791:3140297] 董事1審核合同gcd_demo[41791:3140296] 董事2審核合同gcd_demo[41791:3140320] 董事3審核合同

在這里凯正,我們可以將meetingQueue看成是會(huì)議的時(shí)間線毙玻。總裁簽字這個(gè)行為相當(dāng)于寫操作廊散,其他都相當(dāng)于讀操作桑滩。使用dispatch_barrier_async以后,之前的所有并發(fā)任務(wù)都會(huì)被dispatch_barrier_async里的任務(wù)攔截掉允睹,就像函數(shù)名稱里的“柵欄”一樣运准。

因此,使用Concurrent Dispatch Queue 和 dispatch_barrier_async 函數(shù)可以實(shí)現(xiàn)高效率的數(shù)據(jù)庫訪問和文件訪問缭受。

dispatch_sync

到目前為止的所有例子都使用的是異步函數(shù)胁澳,有異步就一定會(huì)有同步,那么現(xiàn)在就來區(qū)分一下同步和異步函數(shù)的區(qū)別:

·dispatch_async:異步函數(shù)米者,這個(gè)函數(shù)會(huì)立即返回韭畸,不做任何等待,它所指定的block“非同步地”追加到指定的隊(duì)列中蔓搞。

·dispatch_sync:同步函數(shù)胰丁,這個(gè)函數(shù)不會(huì)立即返回,它會(huì)一直等待追加到特定隊(duì)列中的制定block完成工作后才返回喂分,所以它的目的(也是效果)是阻塞當(dāng)前線程锦庸。

舉個(gè)例子:

- (void)dispatch_sync_1{//同步處理NSLog(@"%@",[NSThreadcurrentThread]);NSLog(@"同步處理開始");? ? __blockNSIntegernum =0;dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);dispatch_sync(queue, ^{//模仿耗時(shí)操作for(NSIntegeri =0; i<1000000000; i ++) {? ? ? ? ? ? num++;? ? ? ? }NSLog(@"%@",[NSThreadcurrentThread]);NSLog(@"同步處理完畢");? ? });NSLog(@"%ld",num);NSLog(@"%@",[NSThreadcurrentThread]);}

輸出結(jié)果:

gcd_demo[5604:188687] {number =1, name = main}gcd_demo[5604:188687] 同步處理開始gcd_demo[5604:188687] {number =1, name = main}gcd_demo[5604:188687] 同步處理完畢gcd_demo[5604:188687]1000000000gcd_demo[5604:188687] {number =1, name = main}

在最開始的時(shí)候只打印前兩行,循環(huán)完畢之后才打印后面的內(nèi)容妻顶。

因?yàn)槭峭胶瘮?shù)酸员,它阻塞了當(dāng)前線程(主線程),所以只能等到block內(nèi)部的任務(wù)都結(jié)束后讳嘱,才能打印下面的兩行幔嗦。

但是如果使用異步函數(shù)會(huì)怎樣呢?

- (void)dispatch_sync_2{//異步處理NSLog(@"%@",[NSThreadcurrentThread]);NSLog(@"異步處理開始");? ? __blockNSIntegernum =0;dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);dispatch_async(queue, ^{//模仿耗時(shí)操作for(NSIntegeri =0; i<1000000000; i ++) {? ? ? ? ? ? num++;? ? ? ? }NSLog(@"%@",[NSThreadcurrentThread]);NSLog(@"異步處理完畢");? ? });NSLog(@"%ld",num);NSLog(@"%@",[NSThreadcurrentThread]);}

輸出:

gcd_demo[5685:194233] {number =1, name = main}gcd_demo[5685:194233] 異步處理開始gcd_demo[5685:194233]0gcd_demo[5685:194233] {number =1, name = main}gcd_demo[5685:194280] {number =3, name = (null)}gcd_demo[5685:194280] 異步處理完畢

我們可以看到沥潭,不同于上面的情況邀泉,block下面的兩個(gè)輸出是先打印的(因?yàn)闆]有經(jīng)過for循環(huán)的計(jì)算,num的值是0)。因?yàn)槭钱惒教幚砘阈簦詻]有等待block中任務(wù)的完成就立即返回了庞钢。

了解了同步異步的區(qū)別之后,我們看一下使用同步函數(shù)容易發(fā)生的問題:如果給同步函數(shù)傳入的隊(duì)列是串行隊(duì)列的時(shí)候就會(huì)容易造成死鎖因谎』ǎ看一下一個(gè)死鎖的例子:

- (void)dispatch_sync_3{NSLog(@"任務(wù)1");dispatch_queue_tqueue = dispatch_get_main_queue();dispatch_sync(queue, ^{NSLog(@"任務(wù)2");? ? });NSLog(@"任務(wù)3");}

上面的代碼只能輸出任務(wù)1,并形成死鎖财岔。

因?yàn)槿蝿?wù)2被追加到了主隊(duì)列的最后风皿,所以它需要等待任務(wù)3執(zhí)行完成。

但又因?yàn)槭峭胶瘮?shù)匠璧,任務(wù)3也在等待任務(wù)2執(zhí)行完成桐款。

二者互相等待,所以形成了死鎖夷恍。

dispatch_apply

通過dispatch_apply函數(shù)魔眨,我們可以按照指定的次數(shù)將block追加到指定的隊(duì)列中。并等待全部處理執(zhí)行結(jié)束酿雪。

- (void)dispatch_apply_1{dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);? ? dispatch_apply(10, queue, ^(size_t index) {NSLog(@"%ld",index);? ? });NSLog(@"完畢");}

gcd_demo[6128:240332]1gcd_demo[6128:240331]0gcd_demo[6128:240334]2gcd_demo[6128:240332]4gcd_demo[6128:240334]6gcd_demo[6128:240331]5gcd_demo[6128:240332]7gcd_demo[6128:240334]8gcd_demo[6128:240331]9gcd_demo[6128:240259]3gcd_demo[6128:240259] 完畢

我們也可以用這個(gè)函數(shù)來遍歷數(shù)組遏暴,取得下標(biāo)進(jìn)行操作:

- (void)dispatch_apply_2{NSArray*array = @[@1,@10,@43,@13,@33];dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);? ? dispatch_apply([array count], queue, ^(size_t index) {NSLog(@"%@",array[index]);? ? });NSLog(@"完畢");}

輸出:

gcd_demo[6180:244316]10gcd_demo[6180:244313]1gcd_demo[6180:244316]33gcd_demo[6180:244314]43gcd_demo[6180:244261]13gcd_demo[6180:244261] 完畢

我們可以看到dispatch_apply函數(shù)與dispatch_sync函數(shù)同樣具有阻塞的作用(dispatch_apply函數(shù)返回后才打印完畢)。

我們也可以在dispatch_async函數(shù)里執(zhí)行dispatch_apply函數(shù):

- (void)dispatch_apply_3{dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);dispatch_async(queue, ^{NSArray*array = @[@1,@10,@43,@13,@33];? ? ? ? __blockNSIntegersum =0;? ? ? ? dispatch_apply([array count], queue, ^(size_t index) {NSNumber*number = array[index];NSIntegernum = [number integerValue];? ? ? ? ? ? sum += num;? ? ? ? });dispatch_async(dispatch_get_main_queue(), ^{//回到主線程执虹,拿到總和NSLog(@"完畢");NSLog(@"%ld",sum);? ? ? ? });? ? });}

dispatch_suspend/dispatch_resume

掛起函數(shù)調(diào)用后對已經(jīng)執(zhí)行的處理沒有影響拓挥,但是追加到隊(duì)列中但是尚未執(zhí)行的處理會(huì)在此之后停止執(zhí)行。

dispatch_suspend(queue);

dispatch_resume(queue);

dispatch_once

通過dispatch_once處理的代碼只執(zhí)行一次袋励,而且是線程安全的:

- (void)dispatch_once_1{dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);for(NSIntegerindex =0; index <5; index++) {dispatch_async(queue, ^{? ? ? ? ? ? [selfonceCode];? ? ? ? });? ? }}- (void)onceCode{staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{NSLog(@"只執(zhí)行一次的代碼");? ? });}

輸出:

gcd_demo[7556:361196] 只執(zhí)行一次的代碼

該函數(shù)主要用于單例模式的使用侥啤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茬故,隨后出現(xiàn)的幾起案子盖灸,更是在濱河造成了極大的恐慌,老刑警劉巖磺芭,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赁炎,死亡現(xiàn)場離奇詭異,居然都是意外死亡钾腺,警方通過查閱死者的電腦和手機(jī)徙垫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來放棒,“玉大人姻报,你說我怎么就攤上這事〖涿” “怎么了吴旋?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵损肛,是天一觀的道長。 經(jīng)常有香客問我荣瑟,道長治拿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任笆焰,我火速辦了婚禮劫谅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仙辟。我一直安慰自己同波,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布叠国。 她就那樣靜靜地躺著,像睡著了一般戴尸。 火紅的嫁衣襯著肌膚如雪粟焊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天孙蒙,我揣著相機(jī)與錄音项棠,去河邊找鬼。 笑死挎峦,一個(gè)胖子當(dāng)著我的面吹牛香追,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坦胶,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼透典,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了顿苇?” 一聲冷哼從身側(cè)響起峭咒,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纪岁,沒想到半個(gè)月后凑队,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幔翰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年漩氨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遗增。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叫惊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贡定,到底是詐尸還是另有隱情赋访,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蚓耽,受9級(jí)特大地震影響渠牲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜步悠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一签杈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鼎兽,春花似錦答姥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至择卦,卻和暖如春敲长,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秉继。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工祈噪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尚辑。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓辑鲤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杠茬。 傳聞我的和親對象是個(gè)殘疾皇子月褥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容