一、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)大家指正。