OC中GCD使用

一、GCD的使用:

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

async表明異步運(yùn)行,block代表的是你要做的事情,queue則是你把任務(wù)交給誰(shuí)來處理了.當(dāng)程序異步執(zhí)行時(shí),如果是并發(fā)隊(duì)列執(zhí)行那么到底開辟多少線程有系統(tǒng)決定,如果是在同步隊(duì)列執(zhí)行的話只會(huì)開啟一條子線程。

之所以程序中會(huì)用到多線程是因?yàn)槌绦蛲鶗?huì)需要下載數(shù)據(jù),然后更新UI.為了良好的用戶體驗(yàn),讀取數(shù)據(jù)的操作會(huì)傾向于在后臺(tái)運(yùn)行,這樣以避免阻塞主線程.

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

sync表明同步運(yùn)行,block代表的是你要做的事情,queue則是你把任務(wù)交給誰(shuí)來處理了.需要注意的是,同步執(zhí)行時(shí)無(wú)論是在dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);(全局并發(fā)隊(duì)列還是自己創(chuàng)建的并發(fā)隊(duì)列)系統(tǒng)都不會(huì)幫我們開啟子線程,所有操作都是在主隊(duì)列執(zhí)行。但切記不能在dispatch_get_main_queue()隊(duì)列執(zhí)行,否則會(huì)造成死鎖,這時(shí)候系統(tǒng)不知道應(yīng)該先執(zhí)行上面的操作,還是先執(zhí)行下面的操作。

(1)首先給大家介紹下dispatch_queue

系統(tǒng)默認(rèn)就有一個(gè)串行隊(duì)列main_queue和并行隊(duì)列g(shù)lobal_queue:

dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_queue_t mainQ = dispatch_get_main_queue();

當(dāng)然我們也可以手動(dòng)創(chuàng)建dispatch_queue:

Serial Dispatch Queue -- 線程池只提供一個(gè)子線程用來執(zhí)行任務(wù),所以后一個(gè)任務(wù)必須等到前一個(gè)任務(wù)執(zhí)行結(jié)束才能開始。

驗(yàn)證:

dispatch_queue_t serial = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);

dispatch_async(serial, ^{

sleep(5);

NSLog(@"1 queue=%@",[NSThread currentThread]);

});

dispatch_async(serial, ^{

sleep(3);

NSLog(@"2 queue=%@",[NSThread currentThread]);

});

dispatch_async(serial, ^{

sleep(1);

NSLog(@"3 queue=%@",[NSThread currentThread]);

});

打印結(jié)果:

15:51:04.004 TestReplaykit[8802:223039] 1 queue={number = 2, name = (null)}

15:51:07.010 TestReplaykit[8802:223039] 2 queue={number = 2, name = (null)}

15:51:08.015 TestReplaykit[8802:223039] 3 queue={number = 2, name = (null)}

Concurrent Dispatch Queue -- 線程池提供多個(gè)線程來執(zhí)行任務(wù),所以可以按序啟動(dòng)多個(gè)任務(wù)并發(fā)執(zhí)行。

驗(yàn)證:

dispatch_queue_t serial = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(serial, ^{

sleep(5);

NSLog(@"1 queue=%@",[NSThread currentThread]);

});

dispatch_async(serial, ^{

sleep(3);

NSLog(@"2 queue=%@",[NSThread currentThread]);

});

dispatch_async(serial, ^{

sleep(1);

NSLog(@"3 queue=%@",[NSThread currentThread]);

});

15:48:14.605 TestReplaykit[8746:221997] 3 queue={number = 2, name = (null)}

15:48:16.608 TestReplaykit[8746:221999] 2 queue={number = 3, name = (null)}

15:48:18.607 TestReplaykit[8746:221995] 1 queue={number = 4, name = (null)}

(2)global_queue和Main queue的簡(jiǎn)單使用:

在開發(fā)中我們之所以用到多線程,是因?yàn)楹芏嗪臅r(shí)操作會(huì)阻塞主線程,造成頁(yè)面假死,為了更好的用戶體驗(yàn)我們會(huì)把這些耗時(shí)操作放到global_queue中來完成,完成后我們必須回到主線程中來刷新UI,所以在開發(fā)中凡是涉及到UI的邏輯我們都要把代碼放到main_queue中來處理

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

? ? ? ? ?NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];

? ? ? ? ?NSData * data = [[NSData alloc]initWithContentsOfURL:url];

? ? ? ? ?UIImage *image = [[UIImage alloc]initWithData:data];

? ? ? ? if (data != nil) {

? ? ? ? ? ? ? ? ? ? dispatch_async(dispatch_get_main_queue(), ^{

? ? ? ? ? ? ? ? ? ?//回到主線程刷新UI

? ? ? ? ? ? ? ? ? ?self.imageView.image = image;

? ? ? ? ? ? ? });

? ? ? ? }

});

(3)dispatch_group_async的使用

dispatch_group_async可以實(shí)現(xiàn)監(jiān)聽一組任務(wù)是否完成,完成后得到通知執(zhí)行其他的操作。這個(gè)方法很常用,比如你執(zhí)行多個(gè)下載任務(wù),當(dāng)任務(wù)都下載完成后你才通知界面說完成的了。下面是一段例子代碼:

dispatch_queue_t serial = dispatch_get_global_queue(0, 0);

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,serial, ^{

sleep(5);

NSLog(@"1 queue=%@",[NSThread currentThread]);

});

dispatch_group_async(group,serial, ^{

sleep(3);

NSLog(@"2 queue=%@",[NSThread currentThread]);

});

dispatch_group_async(group,serial, ^{

sleep(1);

NSLog(@"3 queue=%@",[NSThread currentThread]);

});

dispatch_group_notify(group, serial, ^{

NSLog(@"4 queue=%@",[NSThread currentThread]);

});

15:59:51.063 TestReplaykit[8978:225207] 3 queue={number = 2, name = (null)}

15:59:53.062 TestReplaykit[8978:225208] 2 queue={number = 3, name = (null)

?15:59:55.062 TestReplaykit[8978:225206] 1 queue={number = 4, name = (null)}

?15:59:55.063 TestReplaykit[8978:225206] 4 queue={number = 4, name = (null)}

(4)棧欄函數(shù)dispatch_barrier_async

dispatch_queue_t serial = dispatch_get_global_queue(0, 0);

dispatch_async(serial, ^{

sleep(5);

NSLog(@"1 queue=%@",[NSThread currentThread]);

});

dispatch_async(serial, ^{

sleep(3);

NSLog(@"2 queue=%@",[NSThread currentThread]);

});

dispatch_barrier_async(serial, ^{

sleep(1);

NSLog(@"4 queue=%@",[NSThread currentThread]);

});

dispatch_async(serial, ^{

sleep(1);

NSLog(@"3 queue=%@",[NSThread currentThread]);

});

運(yùn)行結(jié)果:

16:40:24.026 TestReplaykit[9775:234799] 4 queue={number = 2, name = (null)}

?16:40:24.026 TestReplaykit[9775:234802] 3 queue={number = 3, name = (null)}

16:40:26.025 TestReplaykit[9775:234798] 2 queue={number = 4, name = (null)}

16:40:28.025 TestReplaykit[9775:234800] 1 queue={number = 5, name = (null)}

如果使用dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL);

根據(jù)FIFO原則肯定為順序執(zhí)行,感興趣的同學(xué)可以自己驗(yàn)證下。

如果使用dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);

dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);

dispatch_async(serial, ^{

sleep(5);

NSLog(@"1 queue=%@",[NSThread currentThread]);

});

dispatch_async(serial, ^{

sleep(3);

NSLog(@"2 queue=%@",[NSThread currentThread]);

});

dispatch_barrier_async(serial, ^{

sleep(1);

NSLog(@"4 queue=%@",[NSThread currentThread]);

});

dispatch_async(serial, ^{

sleep(1);

NSLog(@"3 queue=%@",[NSThread currentThread]);

});

16:45:07.788 TestReplaykit[9878:236344] 2 queue={number = 2, name = (null)}

16:45:09.788 TestReplaykit[9878:236347] 1 queue={number = 3, name = (null)}

16:45:10.792 TestReplaykit[9878:236344] 4 queue={number = 2, name = (null)}

16:45:11.795 TestReplaykit[9878:236347] 3 queue={number = 3, name = (null)}

由上我們可以得出一個(gè)結(jié)論,我們可以通過棧欄函數(shù)來控制子線程執(zhí)行順序,但是queue不能是系統(tǒng)的global_queue,只能是自己創(chuàng)建的queue_create類型,同時(shí)參數(shù)只能是DISPATCH_QUEUE_CONCURRENT

(5)dispatch_once

用處也很多,它會(huì)讓我們的某個(gè)操作在生命周期中只執(zhí)行一次,常用在單例模式中

static dispatch_once_t onceToken;

dispatch_once(&onceToken,?^{

? ? // 執(zhí)行一次

});

(6)dispatch_after

如果我們需要做一些延時(shí)操作是可以通過dispatch_after來完成

float delaySecond = 2.0;

?dispatch_time_t?popTime?=?dispatch_time(DISPATCH_TIME_NOW,?delayInSeconds?*?NSEC_PER_SEC);

dispatch_after(popTime,?dispatch_get_main_queue(),?^(void){

?? ? ? ?//?code?to?be?executed?on?the?main?queue?after?delay

});

(7)GCD定時(shí)器,不受runloop影響,雖然稍微復(fù)雜點(diǎn),但是效率還是很高的

// 獲得隊(duì)列

dispatch_queue_t queue = dispatch_get_main_queue();

// 創(chuàng)建一個(gè)定時(shí)器,這里的定時(shí)器(dispatch_source_t類型)其實(shí)是個(gè)OC對(duì)象,所以必須強(qiáng)引用

self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

// 設(shè)置定時(shí)器的各種屬性

// GCD的時(shí)間參數(shù),一般是納秒,NSEC_PER_SEC=10的9次方納秒

//何時(shí)開始執(zhí)行第一個(gè)任務(wù),比當(dāng)前時(shí)間晚1秒,

dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));

uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);//每隔多長(zhǎng)時(shí)間執(zhí)行一次

dispatch_source_set_timer(self.timer, start, interval, 0);

// 設(shè)置回調(diào)

dispatch_source_set_event_handler(self.timer, ^{

NSLog(@"------------%@", [NSThread currentThread]);

count++;

// 取消定時(shí)器

dispatch_cancel(self.timer);

self.timer = nil;

});

// 啟動(dòng)定時(shí)器

dispatch_resume(self.timer);

小結(jié):

dispatch_async+dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL);開啟一條子線程

dispatch_async+dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);系統(tǒng)決定

dispatch_async+dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);系統(tǒng)決定

dispatch_async+dispatch_get_main_queue();不會(huì)開啟子線程


dispatch_sync+dispatch_get_main_queue();線程死鎖

dispatch_sync+其他;都不會(huì)開啟子線程

如果線程之間有依賴關(guān)系,可以通過棧欄函數(shù)或者dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL)來完成。

如果需要監(jiān)聽子線程,可以通過dispatch_group_t來完成。

如有不足的地方請(qǐng)大家指正。

最后編輯于
?著作權(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)容