GCD是Apple開發(fā)的一個(gè)多核編程的解決方案唆垃,他可以用于多核的并行運(yùn)算接校,并自動(dòng)利用更多的CPU內(nèi)核筹吐,同時(shí)GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程糖耸、調(diào)度任務(wù)、銷毀線程)丘薛,而且我們使用GCD時(shí)嘉竟,只需要調(diào)用GCD的API來填入需要執(zhí)行的任務(wù),而不需要編寫任何線程管理的代碼
一洋侨、任務(wù)和隊(duì)列
任務(wù):
執(zhí)行的操作舍扰,任務(wù)分為同步執(zhí)行和異步執(zhí)行兩種。
同步(sync):同步添加任務(wù)到指定的隊(duì)列中希坚,在添加的任務(wù)執(zhí)行結(jié)束之前边苹,會(huì)一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行裁僧,即會(huì)阻塞線程个束。只能在當(dāng)前線程中執(zhí)行任務(wù)(是當(dāng)前線程慕购,不一定是主線程),不具備開啟新線程的能力茬底。API為dispatch_sync這類函數(shù)
異步(async):線程會(huì)立即返回沪悲,無需等待就會(huì)繼續(xù)執(zhí)行下面的任務(wù),不阻塞當(dāng)前線程阱表〉钊纾可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力(并不一定開啟新線程)捶枢。如果不是添加到主隊(duì)列上握截,異步會(huì)在子線程中執(zhí)行任務(wù)。API為dispatch_async這類函數(shù)
隊(duì)列
隊(duì)列:存放任務(wù)的隊(duì)列烂叔,隊(duì)列是一種特殊的線性表谨胞,遵從FIFO(First in first out,先進(jìn)先出)原則,分為兩種:串行隊(duì)列和并行隊(duì)列
并行隊(duì)列(concurrent):多個(gè)任務(wù)同時(shí)執(zhí)行(每個(gè)任務(wù)在不同的線程中執(zhí)行),API為dispatch_async這類函數(shù)
串行隊(duì)列(serial):任務(wù)按照順序執(zhí)行(各個(gè)任務(wù)可能在同一個(gè)線程蒜鸡,也可能在不同的線程)胯努,API為dispatch_sync這類函數(shù)
二、GCD四種基礎(chǔ)操作
1.串行隊(duì)列 + 同步執(zhí)行
- (void)dispatchSyncSerial{
//這個(gè)函數(shù)創(chuàng)建隊(duì)列逢防,第一個(gè)參數(shù)為隊(duì)列名叶沛,第二個(gè)參數(shù)標(biāo)記創(chuàng)建的隊(duì)列是串行隊(duì)列還是并行隊(duì)列,使用兩個(gè)宏定義來創(chuàng)建忘朝,DISPATCH_QUEUE_SERIAL為串行隊(duì)列灰署,DISPATCH_QUEUE_CONCURRENT為并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("串行隊(duì)列名", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"在串行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"在串行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"在串行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"在串行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
}
/* 結(jié)果
2017-08-22 18:42:55.397 GCD簡(jiǎn)介與Demo[4463:266445] 在串行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:{number = 1, name = main}
2017-08-22 18:42:55.398 GCD簡(jiǎn)介與Demo[4463:266445] 在串行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:{number = 1, name = main}
2017-08-22 18:42:57.399 GCD簡(jiǎn)介與Demo[4463:266445] 在串行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:{number = 1, name = main}
2017-08-22 18:42:57.400 GCD簡(jiǎn)介與Demo[4463:266445] 在串行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:{number = 1, name = main}
*/
結(jié)論:串行隊(duì)列+同步執(zhí)行,任務(wù)按照添加的順序局嘁,依次執(zhí)行,并且都在同一個(gè)線程中執(zhí)行
2.串行隊(duì)列 + 異步執(zhí)行
- (void)dispatchAsyncSerial{
dispatch_queue_t queue = dispatch_queue_create("串行隊(duì)列名", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"在串行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"在串行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
sleep(2);
NSLog(@"在串行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"在串行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
}
/* 結(jié)果
2017-08-22 18:43:45.006 GCD簡(jiǎn)介與Demo[4481:267419] 在串行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:{number = 3, name = (null)}
2017-08-22 18:43:45.006 GCD簡(jiǎn)介與Demo[4481:267419] 在串行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:{number = 3, name = (null)}
2017-08-22 18:43:47.011 GCD簡(jiǎn)介與Demo[4481:267419] 在串行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:{number = 3, name = (null)}
2017-08-22 18:43:47.011 GCD簡(jiǎn)介與Demo[4481:267419] 在串行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:{number = 3, name = (null)}
*/
結(jié)論:串行隊(duì)列 + 異步執(zhí)行溉箕,雖然各個(gè)任務(wù)都在在子線程中執(zhí)行,但仍然是按照添加入隊(duì)列的順序悦昵,依次執(zhí)行
3.并行隊(duì)列 + 同步執(zhí)行
- (void)dispatchSyncConcurrent{
dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"在并行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"在并行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"在并行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"在并行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
}
/*
結(jié)果
2017-08-22 18:48:30.946 GCD簡(jiǎn)介與Demo[4535:272013] 在并行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:<NSThread: 0x60800006d840>{number = 1, name = main}
2017-08-22 18:48:30.947 GCD簡(jiǎn)介與Demo[4535:272013] 在并行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:<NSThread: 0x60800006d840>{number = 1, name = main}
2017-08-22 18:48:32.948 GCD簡(jiǎn)介與Demo[4535:272013] 在并行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:<NSThread: 0x60800006d840>{number = 1, name = main}
2017-08-22 18:48:32.949 GCD簡(jiǎn)介與Demo[4535:272013] 在并行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:<NSThread: 0x60800006d840>{number = 1, name = main}
*/
結(jié)論:并行隊(duì)列 + 同步執(zhí)行肴茄,雖然隊(duì)里是并行隊(duì)列,但是由于是同步執(zhí)行但指,所有任務(wù)都在主線程中執(zhí)行寡痰,他們?nèi)匀皇前凑仗砑尤腙?duì)列的順序,依次執(zhí)行
4.并行隊(duì)列 + 異步執(zhí)行
- (void)dispatchAsyncConcurrent{
dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"在并行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"在并行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
sleep(2);
NSLog(@"在并行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"在并行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
}
/*
結(jié)果
2017-08-22 18:51:02.246 GCD簡(jiǎn)介與Demo[4574:274917] 在并行隊(duì)列中執(zhí)行第二個(gè)任務(wù),線程:<NSThread: 0x608000272840>{number = 3, name = (null)}
2017-08-22 18:51:02.246 GCD簡(jiǎn)介與Demo[4574:274937] 在并行隊(duì)列中執(zhí)行第四個(gè)任務(wù),線程:<NSThread: 0x600000267980>{number = 4, name = (null)}
2017-08-22 18:51:03.249 GCD簡(jiǎn)介與Demo[4574:274918] 在并行隊(duì)列中執(zhí)行第一個(gè)任務(wù),線程:<NSThread: 0x600000265ac0>{number = 5, name = (null)}
2017-08-22 18:51:04.250 GCD簡(jiǎn)介與Demo[4574:274920] 在并行隊(duì)列中執(zhí)行第三個(gè)任務(wù),線程:<NSThread: 0x608000266240>{number = 6, name = (null)}
*/
結(jié)論:并行隊(duì)列 + 異步執(zhí)行棋凳,各個(gè)任務(wù)都在子線程中執(zhí)行拦坠,并且同時(shí)開始執(zhí)行
5.GCD中的特殊隊(duì)列
主隊(duì)列:GCD自帶的一個(gè)特殊的串行隊(duì)列,使用dispatch_get_main_queue()獲取
全局隊(duì)列:GCD自帶的一個(gè)特殊的并行隊(duì)列剩岳,使用dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)獲取
注意:DISPATCH_QUEUE_PRIORITY_DEFAULT是一個(gè)宏贪婉,它標(biāo)記隊(duì)列的優(yōu)先級(jí),有以下四種:
DISPATCH_QUEUE_PRIORITY_HIGH 高優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_DEFAULT 默認(rèn)優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_LOW 低優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后臺(tái)優(yōu)先級(jí)
三、線程間的通信以及單次執(zhí)行方法
1.線程間的通信
一般用于在將網(wǎng)絡(luò)請(qǐng)求卢肃、下載疲迂、上傳等耗時(shí)操作放入子線程執(zhí)行完畢后,回到主線程更新UI
注意:UI操作必須在主線程進(jìn)行操作
- (void)gcdSendMessageFromAnotherThread{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"開始執(zhí)行耗時(shí)操作,所處線程:%@",[NSThread currentThread]);
sleep(3);
NSLog(@"耗時(shí)操作執(zhí)行完畢莫湘,開始進(jìn)入主線程");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"進(jìn)入主線程尤蒿,開始執(zhí)行更新UI操作,所處線程:%@",[NSThread currentThread]);
});
});
}
/*
結(jié)果
2017-08-22 19:07:09.099 GCD簡(jiǎn)介與Demo[4698:290444] 開始執(zhí)行耗時(shí)操作,所處線程:<NSThread: 0x600000263600>{number = 3, name = (null)}
2017-08-22 19:07:12.103 GCD簡(jiǎn)介與Demo[4698:290444] 耗時(shí)操作執(zhí)行完畢,開始進(jìn)入主線程
2017-08-22 19:07:12.103 GCD簡(jiǎn)介與Demo[4698:290380] 進(jìn)入主線程幅垮,開始執(zhí)行更新UI操作,所處線程:<NSThread: 0x600000078f40>{number = 1, name = main}
*/
2.單次執(zhí)行 dispatch_once 一般用于創(chuàng)建單例
- (void)dispatchOnce{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"這里是只執(zhí)行一次的代碼,所處線程:%@",[NSThread currentThread]);
});
}
- (void)testDispatchOnce{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"第一次,所處線程:%@",[NSThread currentThread]);
[self dispatchOnce];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"第二次,所處線程:%@",[NSThread currentThread]);
[self dispatchOnce];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"第三次,所處線程:%@",[NSThread currentThread]);
[self dispatchOnce];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"第四次,所處線程:%@",[NSThread currentThread]);
[self dispatchOnce];
});
}
/*
結(jié)果
2017-08-22 19:12:15.813 GCD簡(jiǎn)介與Demo[4729:295603] 第三次,所處線程:<NSThread: 0x6000002604c0>{number = 5, name = (null)}
2017-08-22 19:12:15.813 GCD簡(jiǎn)介與Demo[4729:295617] 第四次,所處線程:<NSThread: 0x600000260600>{number = 6, name = (null)}
2017-08-22 19:12:15.813 GCD簡(jiǎn)介與Demo[4729:295600] 第二次,所處線程:<NSThread: 0x608000263e40>{number = 4, name = (null)}
2017-08-22 19:12:15.813 GCD簡(jiǎn)介與Demo[4729:295601] 第一次,所處線程:<NSThread: 0x608000263f00>{number = 3, name = (null)}
2017-08-22 19:12:15.814 GCD簡(jiǎn)介與Demo[4729:295603] 這里是只執(zhí)行一次的代碼,所處線程:<NSThread: 0x6000002604c0>{number = 5, name = (null)}
*/
結(jié)論:dispatch_once封裝的代碼只在第一次執(zhí)行了一次
四腰池、GCD的其它處理函數(shù)
1.柵欄方法dispatch_barrier
這個(gè)函數(shù)相當(dāng)于一個(gè)停頓,在這個(gè)函數(shù)之前加入隊(duì)列的任務(wù)先執(zhí)行忙芒,執(zhí)行完畢后執(zhí)行柵欄函數(shù)中的任務(wù)示弓,然后再執(zhí)行柵欄函數(shù)之后的任務(wù)
注意:dispatch_barrier_async和dispatch_barrier_sync的區(qū)別:僅僅是執(zhí)行的線程不同,dispatch_barrier_sync會(huì)同步執(zhí)行(在主線程中執(zhí)行)呵萨,dispatch_barrier_async在子線程中執(zhí)行
- (void)dispatchBarrier{
dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"第一個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第二個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
sleep(1);
NSLog(@"執(zhí)行barrier同步任務(wù),線程:%@",[NSThread currentThread]);
sleep(1);
});
dispatch_barrier_async(queue, ^{
sleep(1);
NSLog(@"執(zhí)行barrier異步任務(wù),線程:%@",[NSThread currentThread]);
sleep(1);
});
dispatch_async(queue, ^{
NSLog(@"第三個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第四個(gè)任務(wù),線程:%@",[NSThread currentThread]);
});
}
/*
結(jié)果:
2017-08-22 19:35:16.874 GCD簡(jiǎn)介與Demo[4949:314086] 第一個(gè)任務(wù),線程:<NSThread: 0x60800007b5c0>{number = 3, name = (null)}
2017-08-22 19:35:16.874 GCD簡(jiǎn)介與Demo[4949:314083] 第二個(gè)任務(wù),線程:<NSThread: 0x60800007b680>{number = 4, name = (null)}
2017-08-22 19:35:17.876 GCD簡(jiǎn)介與Demo[4949:313797] 執(zhí)行barrier同步任務(wù),線程:<NSThread: 0x6080000708c0>{number = 1, name = main}
2017-08-22 19:35:19.882 GCD簡(jiǎn)介與Demo[4949:314083] 執(zhí)行barrier異步任務(wù),線程:<NSThread: 0x60800007b680>{number = 4, name = (null)}
2017-08-22 19:35:20.887 GCD簡(jiǎn)介與Demo[4949:314083] 第三個(gè)任務(wù),線程:<NSThread: 0x60800007b680>{number = 4, name = (null)}
2017-08-22 19:35:20.887 GCD簡(jiǎn)介與Demo[4949:314084] 第四個(gè)任務(wù),線程:<NSThread: 0x60800007b7c0>{number = 5, name = (null)}
*/
2.GCD延遲執(zhí)行的方法dispatch_after
- (void)dispatchAfter{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"執(zhí)行子線程任務(wù),線程:%@",[NSThread currentThread]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延時(shí)兩秒后執(zhí)行主線程任務(wù),線程:%@",[NSThread currentThread]);
});
});
}
/*
結(jié)果
2017-08-22 19:40:10.880 GCD簡(jiǎn)介與Demo[4982:319700] 執(zhí)行子線程任務(wù),線程:<NSThread: 0x60000006b8c0>{number = 3, name = (null)}
2017-08-22 19:40:13.072 GCD簡(jiǎn)介與Demo[4982:319625] 延時(shí)兩秒后執(zhí)行主線程任務(wù),線程:<NSThread: 0x6080000674c0>{number = 1, name = main}
*/
3.GCD的快速迭代方法dispatch_apply
dispatch_apply類似for循環(huán)奏属,它是一個(gè)同步調(diào)用,任務(wù)將會(huì)執(zhí)行給定次數(shù)潮峦,如果是并行隊(duì)里中囱皿,那么會(huì)并發(fā)執(zhí)行任務(wù),如果是串行隊(duì)列中忱嘹,則會(huì)順序執(zhí)行其中的代碼
應(yīng)用場(chǎng)景:
防止創(chuàng)建過多線程導(dǎo)致線程爆炸
多個(gè)任務(wù)并行執(zhí)行嘱腥,比如字典數(shù)組的快速解析
- (void)dispatchApplyAsync{
NSArray *array = @[@1,@2,@3,@4,@5];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_apply(array.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%@開始執(zhí)行 %zu times",[NSThread currentThread],index+1);
sleep((unsigned int)(index+1));
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主線程操作");
});
});
}
/*
結(jié)果
2017-08-22 19:54:38.939 GCD簡(jiǎn)介與Demo[5088:331292] <NSThread: 0x600000079940>{number = 5, name = (null)}開始執(zhí)行 3 times
2017-08-22 19:54:38.939 GCD簡(jiǎn)介與Demo[5088:331304] <NSThread: 0x60800007f040>{number = 4, name = (null)}開始執(zhí)行 2 times
2017-08-22 19:54:38.939 GCD簡(jiǎn)介與Demo[5088:331290] <NSThread: 0x600000079840>{number = 3, name = (null)}開始執(zhí)行 1 times
2017-08-22 19:54:38.939 GCD簡(jiǎn)介與Demo[5088:331289] <NSThread: 0x600000079980>{number = 6, name = (null)}開始執(zhí)行 4 times
2017-08-22 19:54:38.940 GCD簡(jiǎn)介與Demo[5088:331290] <NSThread: 0x600000079840>{number = 3, name = (null)}開始執(zhí)行 5 times
2017-08-22 19:54:42.943 GCD簡(jiǎn)介與Demo[5088:331197] 回到主線程操作
*/
可以看出,dispatch_apply之后的操作將會(huì)在dispatch_apply全部執(zhí)行完畢后才開始執(zhí)行
- (void)dispatchApplySync{
NSArray *array = @[@1,@2,@3,@4,@5];
dispatch_queue_t queue = dispatch_queue_create("串行隊(duì)列名", DISPATCH_QUEUE_SERIAL);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_apply(array.count, queue, ^(size_t index) {
NSLog(@"%@開始執(zhí)行 %zu times",[NSThread currentThread],index+1);
sleep((unsigned int)(index+1));
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主線程操作");
});
});
}
/*
結(jié)果
2017-08-22 19:58:03.952 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 1 times
2017-08-22 19:58:04.954 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 2 times
2017-08-22 19:58:06.958 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 3 times
2017-08-22 19:58:09.963 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 4 times
2017-08-22 19:58:13.969 GCD簡(jiǎn)介與Demo[5110:334355] <NSThread: 0x60800006d8c0>{number = 3, name = (null)}開始執(zhí)行 5 times
2017-08-22 19:58:18.972 GCD簡(jiǎn)介與Demo[5110:334292] 回到主線程操作
*/
可以看出拘悦,串行隊(duì)列中齿兔,所有的任務(wù)按照順序依次執(zhí)行,執(zhí)行完畢后础米,再執(zhí)行后面的代碼
4.GCD隊(duì)列組dispatch_group_t
應(yīng)用場(chǎng)景:多個(gè)任務(wù)并發(fā)執(zhí)行分苇,我們需要獲取到全部執(zhí)行完畢后的時(shí)間節(jié)點(diǎn)(使用dispatch_group_notify獲取)
- (void)dispatchGroup{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
NSLog(@"執(zhí)行完第一個(gè)任務(wù)");
});
dispatch_group_async(group, queue, ^{
sleep(1);
NSLog(@"執(zhí)行完第二個(gè)任務(wù)");
});
dispatch_group_async(group, queue, ^{
sleep(2);
NSLog(@"執(zhí)行完第三個(gè)任務(wù)");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"任務(wù)全部執(zhí)行完畢");
});
}
/*
2017-08-22 20:06:04.304 GCD簡(jiǎn)介與Demo[5155:341594] 執(zhí)行完第一個(gè)任務(wù)
2017-08-22 20:06:05.306 GCD簡(jiǎn)介與Demo[5155:341596] 執(zhí)行完第二個(gè)任務(wù)
2017-08-22 20:06:06.306 GCD簡(jiǎn)介與Demo[5155:341593] 執(zhí)行完第三個(gè)任務(wù)
2017-08-22 20:06:06.306 GCD簡(jiǎn)介與Demo[5155:341593] 任務(wù)全部執(zhí)行完畢
*/
五、GCD使用信號(hào)量控制并發(fā)dispatch_semaphore
1.函數(shù)講解:
- dispatch_semaphore_create
函數(shù)原型:dispatch_samaphore_t dispatch_semaphore_create(long value)
傳入的參數(shù)為long椭盏,輸出一個(gè)dispatch_semaphore_t類型且值為value的信號(hào)量组砚。 值得注意的是,這里的傳入的參數(shù)value必須大于或等于0掏颊,否則dispatch_semaphore_create會(huì)返回NULL糟红。
- dispatch_semaphore_signal
函數(shù)原型:long dispatch_semaphore_signal(dispatch_semaphore_tdsema)
這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema的值加1
當(dāng)返回值為0時(shí)表示當(dāng)前并沒有線程等待其處理的信號(hào)量,其處理的信號(hào)量的值加1即可乌叶。當(dāng)返回值不為0時(shí)盆偿,表示其當(dāng)前有(一個(gè)或多個(gè))線程等待其處理的信號(hào)量,并且該函數(shù)喚醒了一個(gè)等待的線程(當(dāng)線程有優(yōu)先級(jí)時(shí)准浴,喚醒優(yōu)先級(jí)最高的線程事扭;否則隨機(jī)喚醒)。
- dispatch_semaphore_wait
函數(shù)原型: long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema的值減1
如果desema的值為0乐横,那么這個(gè)函數(shù)就阻塞當(dāng)前線程等待timeout(注意timeout的類型為dispatch_time_t)
不能直接傳入整形或float型數(shù)求橄,如果等待的期間desema的值被dispatch_semaphore_signal函數(shù)加1了今野,且該函數(shù)(即dispatch_semaphore_wait)所處線程獲得了信號(hào)量,那么就繼續(xù)向下執(zhí)行并將信號(hào)量減1罐农。
如果等待期間沒有獲取到信號(hào)量或者信號(hào)量的值一直為0条霜,那么等到timeout時(shí),其所處線程自動(dòng)執(zhí)行其后語句涵亏。
dispatch_semaphore_wait的返回值也為long型宰睡。當(dāng)其返回0時(shí)表示在timeout之前,該函數(shù)所處的線程被成功喚醒气筋。當(dāng)其返回不為0時(shí)拆内,表示timeout發(fā)生。
2.timeout參數(shù)
DISPATCH_TIME_NOW 表示當(dāng)前(立刻超時(shí))
DISPATCH_TIME_FOREVER 表示遙遠(yuǎn)的未來(永不超時(shí))
具體可參照:關(guān)于dispatch_semaphore的使用
- (void)dispatchSemaphore{
//創(chuàng)建信號(hào)宠默,設(shè)置最大并發(fā)數(shù)為2
dispatch_semaphore_t sem = dispatch_semaphore_create(2);
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
long a = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"第%d個(gè)任務(wù)執(zhí)行,此時(shí)a為:%ld",i+1,a);
sleep(1);
long b = dispatch_semaphore_signal(sem);
NSLog(@"b為:%ld",b);
});
}
}
/*
結(jié)果
2017-08-22 21:11:48.928 GCD簡(jiǎn)介與Demo[716:12343] 第2個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:48.928 GCD簡(jiǎn)介與Demo[716:12330] 第1個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:49.931 GCD簡(jiǎn)介與Demo[716:12329] 第4個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:49.931 GCD簡(jiǎn)介與Demo[716:12332] 第3個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:50.933 GCD簡(jiǎn)介與Demo[716:12345] 第5個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:50.933 GCD簡(jiǎn)介與Demo[716:12346] 第6個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:51.937 GCD簡(jiǎn)介與Demo[716:12347] 第7個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:51.937 GCD簡(jiǎn)介與Demo[716:12348] 第8個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:52.940 GCD簡(jiǎn)介與Demo[716:12350] 第10個(gè)任務(wù)執(zhí)行
2017-08-22 21:11:52.940 GCD簡(jiǎn)介與Demo[716:12349] 第9個(gè)任務(wù)執(zhí)行
*/
可以看出麸恍,同時(shí)執(zhí)行任務(wù)的只有兩個(gè),這就控制了最大并發(fā)數(shù)
六光稼、GCD定時(shí)器 dispatch_source_set_timer
NSTimer是我們最為熟悉的定時(shí)器或南,但是NSTimer有很大的缺點(diǎn),并不準(zhǔn)確艾君。而GCD定時(shí)器采够,則是嚴(yán)格按照規(guī)定好的規(guī)格去做事,它更為準(zhǔn)確
NSTimer是在RunLoop的基礎(chǔ)上執(zhí)行的冰垄,而RunLoop是在GCD的基礎(chǔ)上實(shí)現(xiàn)的蹬癌,所以GCD可以算是更為高級(jí)
@interface ViewController ()
//這里不需要*號(hào),因?yàn)閐ispatch_source_t實(shí)現(xiàn)中已經(jīng)加進(jìn)了指針*號(hào)
@property (nonatomic,strong) dispatch_source_t timer;
@property (nonatomic,strong) UIView *animationView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_animationView = [[UIView alloc]initWithFrame:CGRectMake(0, self.view.bounds.size.height/2.-50, 100, 100)];
_animationView.backgroundColor = [UIColor redColor];
[self.view addSubview:_animationView];
[self beginGCDTimer];
}
- (void)beginGCDTimer{
//如果定時(shí)器已經(jīng)開啟虹茶,則將它關(guān)閉逝薪,并置空
if (self.timer) {
dispatch_source_cancel(self.timer);
self.timer = nil;
}
//創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("并行隊(duì)列名", DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建定時(shí)器
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//設(shè)置開始時(shí)間 后面的參數(shù)意思為從現(xiàn)在開始后2秒開始執(zhí)行定時(shí)器回調(diào)
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC);
//設(shè)置時(shí)間間隔 uint64_t為unsigned long long的別名,無符號(hào)長(zhǎng)長(zhǎng)整形
uint64_t interval = 0.01 * NSEC_PER_SEC;
//設(shè)置定時(shí)器 最后一個(gè)參數(shù)為偏差蝴罪,一般設(shè)置為0
dispatch_source_set_timer(_timer, start, interval, 0);
//設(shè)置回調(diào)
dispatch_source_set_event_handler(_timer, ^{
CGFloat x = self.animationView.frame.origin.x;
CGFloat width = self.view.bounds.size.width;
x += 1;
if (x >= width) {
x = 0;
}
CGRect frame = self.animationView.frame;
frame.origin.x = x;
dispatch_async(dispatch_get_main_queue(), ^{
self.animationView.frame = frame;
});
});
//定時(shí)器默認(rèn)是暫停的董济,需要手動(dòng)啟動(dòng)
dispatch_resume(_timer);
//如果要退出GCD的定時(shí)器可以使用
//dispatch_source_cancel(_timer);
}
@end
注意,如果需要暫停GCD定時(shí)器要门,則調(diào)用dispatch_suspend(_timer);
虏肾,恢復(fù)則調(diào)用dispatch_resume(_timer);
NSTimer不準(zhǔn)時(shí)的原因:
1.RunLoop循環(huán)處理總會(huì)有時(shí)間
2.受到RunLoop模式的影響GCD Timer與NSTimer是不同的
1.都是源,前者是Dispatch的源欢搜,后者是RunLoop的源
2.GCD Timer不需要像NSTimer那樣加入RunLoop的modeGCD Timer總結(jié)
1.GCD Timer精度高(納米級(jí)別)
2.GCD Timer在主線程執(zhí)行會(huì)受RunLoop影響封豪,子線程不受影響
3.GCD Timer不受模式切換的影響
備注:
關(guān)于時(shí)間的一些宏
#define NSEC_PER_SEC 1000000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
NSEC:納秒。
USEC:微秒炒瘟。
SEC:秒
PER:每
1 吹埠、NSEC_PER_SEC,每秒有多少納秒。
2 缘琅、USEC_PER_SEC粘都,每秒有多少毫秒。(注意是指在納秒的基礎(chǔ)上)
3 胯杭、NSEC_PER_USEC驯杜,每毫秒有多少納秒做个。
七、dispatch_source_t
dispatch_source_t
還有更多更多的用法滚局,我們介紹最常用的一種,使用DISPATCH_SOURCE_TYPE_DATA_ADD
最住,我們可以使用聯(lián)結(jié)來實(shí)現(xiàn)一些神奇的功能
聯(lián)結(jié)的大致流程:在任一線程上調(diào)用它的一個(gè)函數(shù) dispatch_source_merge_data
后,會(huì)執(zhí)行 Dispatch Source 事先定義好的句柄茂翔,而且我們可以在這個(gè)句柄中使用dispatch_source_get_data
獲取到dispatch_source_merge_data
傳遞給我們的值
下面是一個(gè)簡(jiǎn)單的示例:
- (void)viewDidLoad{
[super viewDidLoad];
self.queue = dispatch_queue_create("add", NULL);
self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, self.queue);
dispatch_source_set_event_handler(self.source, ^{
unsigned long data = dispatch_source_get_data(self.source);
NSLog(@"響應(yīng)混蔼,得到值為:%lu",data);
});
dispatch_resume(self.source);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
dispatch_source_merge_data(self.source, 1);
}
從上面示例我們可以想到哪個(gè)使用場(chǎng)景呢?
在我們分片下載一個(gè)大文件時(shí)珊燎,我們可以把各個(gè)分片在異步并發(fā)下載惭嚣,下載時(shí)使用dispatch_source_merge_data
回傳我們的進(jìn)度,然后再句柄回調(diào)中將各個(gè)進(jìn)度相加俐末,這就可以計(jì)算出我們的下載總進(jìn)度了
更多關(guān)于dispatch_source_t
的用法可以參考大神的iOS多線程——Dispatch Source