引言: 越是細(xì)節(jié)越能體現(xiàn)一個(gè)人的嚴(yán)謹(jǐn)搓劫,越是微小越是能看到事物的光芒
1. 隊(duì)列
官方解釋:
DispathQueue是FIFO隊(duì)列春哨,應(yīng)用程序可以以塊對(duì)象的形式向其提交任務(wù)嗤形。調(diào)度隊(duì)列可以串行執(zhí)行任務(wù)屋吨,也可以并發(fā)執(zhí)行任務(wù)。提交給DispathQueue的工作在系統(tǒng)管理的線程池上執(zhí)行棘劣,不用開(kāi)發(fā)者操心在那個(gè)線程上俏让。除了App主線程的調(diào)度隊(duì)列mainQueue外,別的DispathQueue系統(tǒng)不保證它執(zhí)行任務(wù)的時(shí)候一直在一個(gè)具體的線程中茬暇。您可以同步或異步地安排工作項(xiàng)首昔。當(dāng)您同步地計(jì)劃一個(gè)工作項(xiàng)時(shí),代碼將等待該項(xiàng)完成執(zhí)行糙俗。當(dāng)您異步地計(jì)劃工作項(xiàng)時(shí)勒奇,代碼在工作項(xiàng)運(yùn)行到其他地方時(shí)繼續(xù)執(zhí)行。
避免創(chuàng)建過(guò)多線程
如果使用DispathQueue去并發(fā)執(zhí)行任務(wù)時(shí)巧骚,不要再執(zhí)行的時(shí)候阻塞任務(wù)執(zhí)行的線程赊颠。如果阻塞了格二,系統(tǒng)會(huì)再額外創(chuàng)建線程去執(zhí)行別的任務(wù),如果任務(wù)快太多竣蹦,系統(tǒng)可能會(huì)耗盡應(yīng)用程序的線程顶猜。應(yīng)用程序消耗太多線程的另一種方式是創(chuàng)建太多私有并發(fā)調(diào)度隊(duì)列。因?yàn)槊總€(gè)調(diào)度隊(duì)列都消耗線程資源痘括,所以創(chuàng)建額外的并發(fā)調(diào)度隊(duì)列會(huì)加劇線程消耗問(wèn)題驶兜。不要?jiǎng)?chuàng)建私有并發(fā)隊(duì)列,而是將任務(wù)提交到一個(gè)全局global并發(fā)調(diào)度隊(duì)列远寸。對(duì)于串行任務(wù),請(qǐng)將串行隊(duì)列的目標(biāo)設(shè)置為全局并發(fā)隊(duì)列之一屠凶。這樣驰后,您可以保持隊(duì)列的序列化行為,同時(shí)最小化創(chuàng)建線程的獨(dú)立隊(duì)列的數(shù)量矗愧。
dispatch_queue_t dySerial = dispatch_queue_create("自己創(chuàng)建串行隊(duì)列", DISPATCH_QUEUE_SERIAL); //自己創(chuàng)建串行隊(duì)列
dispatch_queue_t dyConcurrent = dispatch_queue_create("自己創(chuàng)建并行隊(duì)列", DISPATCH_QUEUE_CONCURRENT); //自己創(chuàng)建并行隊(duì)列
dispatch_queue_t osMainSerial = dispatch_get_main_queue(); //系統(tǒng)默認(rèn)主隊(duì)列灶芝,就是主線程
dispatch_queue_t osConcurrent = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //系統(tǒng)默認(rèn)的全局并行隊(duì)列
2. 執(zhí)行
//異步執(zhí)行
dispatch_async("傳入隊(duì)列", 任務(wù)block);
//同步執(zhí)行
dispatch_sync("傳入隊(duì)列", 任務(wù)block);
3. 不同執(zhí)行 + 不同隊(duì)列效果
3.1 同步執(zhí)行 + 并行隊(duì)列 不會(huì)創(chuàng)建多線程 順序執(zhí)行
//當(dāng)前線程打印標(biāo)記
NSLog(@"打印當(dāng)前線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"*********同步執(zhí)行 + 并行隊(duì)列 不會(huì)創(chuàng)建多線程 順序執(zhí)行*********");
dispatch_sync(dyConcurrent, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)1線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(dyConcurrent, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)2線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(dyConcurrent, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)3線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
2018-02-26 15:58:11.853843+0800 GCD 系列知識(shí)點(diǎn)[47527:3342566] 打印當(dāng)前線程---<NSThread: 0x600000069600>{number = 1, name = main}
2018-02-26 15:58:11.854101+0800 GCD 系列知識(shí)點(diǎn)[47527:3342566] *********同步執(zhí)行 + 并行隊(duì)列 不會(huì)創(chuàng)建多線程 順序執(zhí)行*********
2018-02-26 15:58:11.854512+0800 GCD 系列知識(shí)點(diǎn)[47527:3342566] 任務(wù)1線程---<NSThread: 0x600000069600>{number = 1, name = main}
2018-02-26 15:58:11.855069+0800 GCD 系列知識(shí)點(diǎn)[47527:3342566] 任務(wù)1線程---<NSThread: 0x600000069600>{number = 1, name = main}
2018-02-26 15:58:11.855570+0800 GCD 系列知識(shí)點(diǎn)[47527:3342566] 任務(wù)2線程---<NSThread: 0x600000069600>{number = 1, name = main}
2018-02-26 15:58:11.856073+0800 GCD 系列知識(shí)點(diǎn)[47527:3342566] 任務(wù)2線程---<NSThread: 0x600000069600>{number = 1, name = main}
2018-02-26 15:58:11.856731+0800 GCD 系列知識(shí)點(diǎn)[47527:3342566] 任務(wù)3線程---<NSThread: 0x600000069600>{number = 1, name = main}
2018-02-26 15:58:11.857079+0800 GCD 系列知識(shí)點(diǎn)[47527:3342566] 任務(wù)3線程---<NSThread: 0x600000069600>{number = 1, name = main}
3.2 異步執(zhí)行 + 并發(fā)隊(duì)列 會(huì)創(chuàng)建多個(gè)線程
NSLog(@"*********異步執(zhí)行 + 并發(fā)隊(duì)列 會(huì)創(chuàng)建多個(gè)線程 不是順序執(zhí)行*********");
dispatch_async(dyConcurrent, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)1線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(dyConcurrent, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)2線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(dyConcurrent, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)3線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
2018-02-26 15:59:55.983225+0800 GCD 系列知識(shí)點(diǎn)[47573:3349914] 打印當(dāng)前線程---<NSThread: 0x600000076140>{number = 1, name = main}
2018-02-26 15:59:55.983441+0800 GCD 系列知識(shí)點(diǎn)[47573:3349914] *********異步執(zhí)行 + 并發(fā)隊(duì)列 會(huì)創(chuàng)建多個(gè)線程 不是順序執(zhí)行*********
2018-02-26 15:59:55.983760+0800 GCD 系列知識(shí)點(diǎn)[47573:3350179] 任務(wù)2線程---<NSThread: 0x604000679b80>{number = 5, name = (null)}
2018-02-26 15:59:55.983795+0800 GCD 系列知識(shí)點(diǎn)[47573:3350180] 任務(wù)1線程---<NSThread: 0x6000002798c0>{number = 4, name = (null)}
2018-02-26 15:59:55.983821+0800 GCD 系列知識(shí)點(diǎn)[47573:3350190] 任務(wù)3線程---<NSThread: 0x60000027a240>{number = 6, name = (null)}
2018-02-26 15:59:55.984116+0800 GCD 系列知識(shí)點(diǎn)[47573:3350179] 任務(wù)2線程---<NSThread: 0x604000679b80>{number = 5, name = (null)}
2018-02-26 15:59:55.984126+0800 GCD 系列知識(shí)點(diǎn)[47573:3350180] 任務(wù)1線程---<NSThread: 0x6000002798c0>{number = 4, name = (null)}
2018-02-26 15:59:55.984190+0800 GCD 系列知識(shí)點(diǎn)[47573:3350190] 任務(wù)3線程---<NSThread: 0x60000027a240>{number = 6, name = (null)}
3.3 同步執(zhí)行 + 串行隊(duì)列 不會(huì)創(chuàng)建新線程
NSLog(@"*********同步執(zhí)行 + 串行隊(duì)列 不會(huì)創(chuàng)建新線程*********");
dispatch_sync(dySerial, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)1線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(dySerial, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)2線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
2018-02-26 16:02:43.429624+0800 GCD 系列知識(shí)點(diǎn)[47637:3363265] 打印當(dāng)前線程---<NSThread: 0x604000078bc0>{number = 1, name = main}
2018-02-26 16:02:43.429817+0800 GCD 系列知識(shí)點(diǎn)[47637:3363265] *********同步執(zhí)行 + 串行隊(duì)列 不會(huì)創(chuàng)建新線程*********
2018-02-26 16:02:43.430040+0800 GCD 系列知識(shí)點(diǎn)[47637:3363265] 任務(wù)1線程---<NSThread: 0x604000078bc0>{number = 1, name = main}
2018-02-26 16:02:43.430238+0800 GCD 系列知識(shí)點(diǎn)[47637:3363265] 任務(wù)1線程---<NSThread: 0x604000078bc0>{number = 1, name = main}
2018-02-26 16:02:43.430346+0800 GCD 系列知識(shí)點(diǎn)[47637:3363265] 任務(wù)2線程---<NSThread: 0x604000078bc0>{number = 1, name = main}
2018-02-26 16:02:43.430466+0800 GCD 系列知識(shí)點(diǎn)[47637:3363265] 任務(wù)2線程---<NSThread: 0x604000078bc0>{number = 1, name = main}
3.4 異步執(zhí)行 + 串行隊(duì)列 只創(chuàng)建了一個(gè)線程(區(qū)別于當(dāng)前線程的新線程)
NSLog(@"*********異步執(zhí)行 + 串行隊(duì)列 只創(chuàng)建了1個(gè)線程*********");
dispatch_async(dySerial, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)1線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(dySerial, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)2線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
2018-02-26 16:04:52.596042+0800 GCD 系列知識(shí)點(diǎn)[47689:3374647] 打印當(dāng)前線程---<NSThread: 0x60000006f000>{number = 1, name = main}
2018-02-26 16:04:52.596243+0800 GCD 系列知識(shí)點(diǎn)[47689:3374647] *********異步執(zhí)行 + 串行隊(duì)列 只創(chuàng)建了1個(gè)線程*********
2018-02-26 16:04:52.596596+0800 GCD 系列知識(shí)點(diǎn)[47689:3375109] 任務(wù)1線程---<NSThread: 0x60000046ab00>{number = 4, name = (null)}
2018-02-26 16:04:52.596912+0800 GCD 系列知識(shí)點(diǎn)[47689:3375109] 任務(wù)1線程---<NSThread: 0x60000046ab00>{number = 4, name = (null)}
2018-02-26 16:04:52.597323+0800 GCD 系列知識(shí)點(diǎn)[47689:3375109] 任務(wù)2線程---<NSThread: 0x60000046ab00>{number = 4, name = (null)}
2018-02-26 16:04:52.597453+0800 GCD 系列知識(shí)點(diǎn)[47689:3375109] 任務(wù)2線程---<NSThread: 0x60000046ab00>{number = 4, name = (null)}
3.5 同步/異步 + 主隊(duì)列dispatch_get_main_queue()執(zhí)行效果
3.5.1 特別注意: 同步執(zhí)行 + 對(duì)于特殊的串行隊(duì)列 dispatch_get_main_queue() 會(huì)出現(xiàn)死鎖
NSLog(@"*********特別注意 異步執(zhí)行 + 對(duì)于特殊的串行隊(duì)列 dispatch_get_main_queue() 會(huì)出現(xiàn)死鎖 *********");
NSLog(@"任務(wù)1");
dispatch_sync(osMainSerial, ^{
NSLog(@"任務(wù)2線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
NSLog(@"任務(wù)3");
Xcode運(yùn)行 項(xiàng)目直接崩潰
首先執(zhí)行任務(wù)1,然后遇到dispatch_sync 同步線程唉韭,當(dāng)前線程進(jìn)入等待夜涕,等待同步線程中的任務(wù)2執(zhí)行完再執(zhí)行任務(wù)3,這個(gè)任務(wù)2是加入到mainQueue主隊(duì)列中属愤。
dispatch_sync(osMainSerial,^(void)()) 是同步一個(gè)任務(wù)到主隊(duì)列中女器,而當(dāng)前去同步的正是主隊(duì)列。因?yàn)殛?duì)列的執(zhí)行是FIFO(先進(jìn)先出)住诸,所以兩個(gè)都在等待對(duì)方完成驾胆,就會(huì)造成死鎖。
3.5.2 如果這個(gè)操作放在非主隊(duì)列中執(zhí)行就不會(huì)有問(wèn)題
//但是如果dispatch_sync(osMainSerial,^void()) 這個(gè)任務(wù)的執(zhí)行是放在 非主線程中(其他子線程) 執(zhí)行的贱呐,就沒(méi)問(wèn)題
//NSThread detachNewThread 開(kāi)辟一個(gè)新線程執(zhí)行方法
[NSThread detachNewThreadSelector:@selector(testOtherThreadGetMainSyncSomething) toTarget:self withObject:nil];
//測(cè)試 同步執(zhí)行 + 主隊(duì)列dispatch_get_main_queue() 放在其他線程中
- (void)testOtherThreadGetMainSyncSomething{
dispatch_queue_t osMainSerial = dispatch_get_main_queue(); //系統(tǒng)默認(rèn)主隊(duì)列丧诺,就是主線程
NSLog(@"任務(wù)1");
dispatch_sync(osMainSerial, ^{
NSLog(@"任務(wù)2線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
NSLog(@"任務(wù)3");
}
2018-02-26 17:58:01.616851+0800 GCD 系列知識(shí)點(diǎn)[50280:3588248] 打印當(dāng)前線程---<NSThread: 0x60400006aac0>{number = 1, name = main}
2018-02-26 17:58:01.617322+0800 GCD 系列知識(shí)點(diǎn)[50280:3588554] 任務(wù)1
2018-02-26 17:58:01.623797+0800 GCD 系列知識(shí)點(diǎn)[50280:3588248] 任務(wù)2線程---<NSThread: 0x60400006aac0>{number = 1, name = main}
2018-02-26 17:58:01.624153+0800 GCD 系列知識(shí)點(diǎn)[50280:3588554] 任務(wù)3
3.6 異步執(zhí)行 + 對(duì)于特殊的串行隊(duì)列 dispatch_get_main_queue() 異步執(zhí)行的時(shí)候 不會(huì)創(chuàng)建新線程,只會(huì)使用當(dāng)前線程
NSLog(@"*********特別注意 異步執(zhí)行 + 對(duì)于特殊的串行隊(duì)列 dispatch_get_main_queue() 異步執(zhí)行的時(shí)候 不會(huì)創(chuàng)建新線程*********");
dispatch_async(osMainSerial, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)1線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(osMainSerial, ^{
for (int i = 0; i < 2; i++){
NSLog(@"任務(wù)2線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
2018-02-26 17:52:39.747531+0800 GCD 系列知識(shí)點(diǎn)[50190:3564074] 打印當(dāng)前線程---<NSThread: 0x60000006bf80>{number = 1, name = main}
2018-02-26 17:52:39.747691+0800 GCD 系列知識(shí)點(diǎn)[50190:3564074] *********特別注意 異步執(zhí)行 + 對(duì)于特殊的串行隊(duì)列 dispatch_get_main_queue() 異步執(zhí)行的時(shí)候 不會(huì)創(chuàng)建新線程*********
2018-02-26 17:52:39.773217+0800 GCD 系列知識(shí)點(diǎn)[50190:3564074] 任務(wù)1線程---<NSThread: 0x60000006bf80>{number = 1, name = main}
2018-02-26 17:52:39.773976+0800 GCD 系列知識(shí)點(diǎn)[50190:3564074] 任務(wù)1線程---<NSThread: 0x60000006bf80>{number = 1, name = main}
2018-02-26 17:52:39.775269+0800 GCD 系列知識(shí)點(diǎn)[50190:3564074] 任務(wù)2線程---<NSThread: 0x60000006bf80>{number = 1, name = main}
2018-02-26 17:52:39.776235+0800 GCD 系列知識(shí)點(diǎn)[50190:3564074] 任務(wù)2線程---<NSThread: 0x60000006bf80>{number = 1, name = main}
4. 關(guān)于死鎖
4.1 在上3.5.1上介紹 同步執(zhí)行(dispatch_sync
) + 主隊(duì)列 = 死鎖奄薇。那么如果同步執(zhí)行的 + 非主隊(duì)列 是否就一定不會(huì)出現(xiàn)死鎖了驳阎?看下面的代碼:
//測(cè)試 同步 + 串行隊(duì)列(非組隊(duì)列) 死鎖
[self testAsyncSerialLockSometing];
//測(cè)試死鎖 同步執(zhí)行 + 串行隊(duì)列(非主隊(duì)列)死鎖的情況
/**
用異步執(zhí)行一個(gè)串行隊(duì)列的到一個(gè)新的線程,然后再同步執(zhí)行這個(gè)串行隊(duì)列馁蒂,會(huì)發(fā)生死鎖
*/
- (void)testAsyncSerialLockSometing{
//這里自己創(chuàng)建一個(gè)串行隊(duì)列new dySerial
dispatch_queue_t dySerial = dispatch_queue_create("new dySerial", DISPATCH_QUEUE_SERIAL); //自己創(chuàng)建串行隊(duì)列
//先異步執(zhí)行這個(gè)串行隊(duì)列呵晚,這樣可以得到一個(gè)子線程。這個(gè)串行隊(duì)列現(xiàn)在是掛載到
dispatch_async(dySerial, ^{
NSLog(@"任務(wù)1線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
//這里再同步執(zhí)行這個(gè)串行隊(duì)列
dispatch_sync(dySerial, ^{
NSLog(@"任務(wù)2線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
NSLog(@"任務(wù)3線程---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
}
2018-02-27 09:57:49.336615+0800 GCD 系列知識(shí)點(diǎn)[55106:3863926] 打印當(dāng)前線程---<NSThread: 0x60400006efc0>{number = 1, name = main}
2018-02-27 09:57:49.336972+0800 GCD 系列知識(shí)點(diǎn)[55106:3864092] 任務(wù)1線程---<NSThread: 0x60400027ca80>{number = 3, name = (null)}
(lldb)
Thread 3: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
會(huì)發(fā)現(xiàn)還是會(huì)出現(xiàn)死鎖的情況远搪,綜上所述不難發(fā)現(xiàn)這種死鎖產(chǎn)生的原因:在串行隊(duì)列A(主隊(duì)列或自己創(chuàng)建的串行隊(duì)列)的線程中同步執(zhí)行一個(gè)任務(wù)劣纲,而且這個(gè)任務(wù)還是放在串行隊(duì)列A中。這樣就會(huì)出現(xiàn)死鎖谁鳍。
4.2 還有一種死鎖癞季,就是當(dāng)前的串行隊(duì)列被阻塞劫瞳,然后同步任務(wù)到這個(gè)隊(duì)列就會(huì)出現(xiàn)
//測(cè)試 主線程被阻塞 死鎖
[self testThreadBusyLockSomething];
- (void)testThreadBusyLockSomething{
dispatch_queue_t dyConcurrent = dispatch_queue_create("dyConcurrent", DISPATCH_QUEUE_CONCURRENT); //自己創(chuàng)建并行隊(duì)列
dispatch_async(dyConcurrent, ^{
NSLog(@"任務(wù)1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)3");
});
NSLog(@"任務(wù)4");
});
NSLog(@"任務(wù)2");
//這里有個(gè)死循環(huán)
while(1){
}
}
2018-02-27 10:38:31.763153+0800 GCD 系列知識(shí)點(diǎn)[55527:3978731] 打印當(dāng)前線程---<NSThread: 0x604000065500>{number = 1, name = main}
2018-02-27 10:38:31.763335+0800 GCD 系列知識(shí)點(diǎn)[55527:3978731] 任務(wù)2
2018-02-27 10:38:31.763358+0800 GCD 系列知識(shí)點(diǎn)[55527:3978980] 任務(wù)1
執(zhí)行順序:
main隊(duì)列中死循環(huán)不結(jié)束,任務(wù)3不執(zhí)行绷柒,任務(wù)3又是global全局隊(duì)列同步過(guò)去的志于,global中同步的任務(wù)3不執(zhí)行完、任務(wù)4是不會(huì)執(zhí)行的废睦。
了解死鎖的發(fā)生時(shí)機(jī)后就很容易理解伺绽,所謂隊(duì)列(串行并行)就是一個(gè)順序執(zhí)行的甬道。所謂同步異步就是決定執(zhí)行這個(gè)隊(duì)列的時(shí)候是否開(kāi)辟新的線程去執(zhí)行嗜湃。
4. 關(guān)于GCD之間的通信奈应,就是一個(gè)概念記住就好。不同的隊(duì)列直接相互通信购披,只需要在執(zhí)行任務(wù)的時(shí)候同步或異步執(zhí)行任務(wù)的時(shí)候調(diào)用別的隊(duì)列傳值杖挣,最常見(jiàn)的就是
// 獲取全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 獲取主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// 異步追加任務(wù)
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
// 回到主線程
dispatch_async(mainQueue, ^{
// 追加在主線程中執(zhí)行的任務(wù)
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
});
5. GCD常用方法
5.1 組操作dispatch_group
5.1.1 dispatch_group_notify、dispatch_group_wait 基本用法
異步執(zhí)行多個(gè)耗時(shí)任務(wù)刚陡,然后多個(gè)任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)惩妇。這時(shí)候我們可以用到 GCD 的隊(duì)列組。
//用于創(chuàng)建任務(wù)組
dispatch_group_t dispatch_group_create(void);
//異步任務(wù)提交到指定任務(wù)組和指定下拿出隊(duì)列執(zhí)行
dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
//多個(gè)異步任務(wù)完成后調(diào)用block(不會(huì)阻塞當(dāng)前線程)
dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
//多個(gè)異步任務(wù)完成后dispatch_group_wait這句話后面的代碼才會(huì)執(zhí)行(會(huì)阻塞當(dāng)前線程筐乳,任務(wù)全部執(zhí)行完畢或超時(shí)才會(huì)接觸阻塞)
//注意:因?yàn)閐ispatch_group_wait會(huì)阻塞當(dāng)前線程歌殃,所以一般情況不要放入主線程
dispatch_group_wait(dispatch_group_t group,
dispatch_time_t timeout);
//使用的時(shí)候成對(duì)使用,在執(zhí)行某個(gè)任務(wù)前調(diào)用enter蝙云,待執(zhí)行任務(wù)數(shù)+1
//在某個(gè)任務(wù)完成后氓皱,調(diào)用leave,待執(zhí)行任務(wù)數(shù)-1
//只有所有任務(wù)都完成(任務(wù)數(shù)為0)才會(huì)觸發(fā)dispatch_group_notify 或 dispatch_group_wait
dispatch_group_enter(dispatch_group_t group);
dispatch_group_leave(dispatch_group_t group);
看Demo
-(void)testGroup{
dispatch_group_t group = dispatch_group_create(); //創(chuàng)建zhu
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"doSomeThing1");
}) ;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"doSomeThing2");
}) ;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"doSomeThing3");
sleep(3);
}) ;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新界面");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"接著做某事");
}
2018-02-28 16:16:29.172002+0800 GCD 系列知識(shí)點(diǎn)[14525:1509846] doSomeThing1
2018-02-28 16:16:29.172004+0800 GCD 系列知識(shí)點(diǎn)[14525:1509848] doSomeThing2
2018-02-28 16:16:29.172014+0800 GCD 系列知識(shí)點(diǎn)[14525:1509849] doSomeThing3
2018-02-28 16:16:32.174314+0800 GCD 系列知識(shí)點(diǎn)[14525:1509719] 接著做某事
2018-02-28 16:16:32.179732+0800 GCD 系列知識(shí)點(diǎn)[14525:1509719] 刷新界面
運(yùn)行發(fā)現(xiàn) 贮懈,所有任務(wù)都執(zhí)行完畢了匀泊,NSLog(@"刷新界面");
這句代碼才執(zhí)行,NSLog(@"接著做某事");
這句代碼才執(zhí)行朵你。
注意
1各聘,dispatch_group_wait
會(huì)阻塞當(dāng)前線程,什么時(shí)候在它之前group_async執(zhí)行的block任務(wù)結(jié)束了抡医,或者wait自己設(shè)置的時(shí)間超時(shí)了躲因,代碼才會(huì)往下執(zhí)行。如果dispatch_group_wait
之前沒(méi)有g(shù)roup_async block任務(wù)忌傻,它就會(huì)馬上執(zhí)行
2大脉,dispatch_group_wait
和 dispatch_group_notify
同時(shí)出現(xiàn),只有dispatch_group_wait
執(zhí)行后水孩, dispatch_group_notify
才會(huì)執(zhí)行镰矿,不管dispatch_group_wait
在 dispatch_group_notify
之前還是之后。
dispatch_group_wait
會(huì)阻塞當(dāng)前線程俘种,所以不要放在App主線程秤标。
5.1.2 dispatch_group_enter绝淡、dispatch_group_leave 基本用法
在介紹這個(gè)用法之前,我們先看一個(gè)例子
-(void)testGroup{
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"doSomeThing1");
});
}) ;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"doSomeThing2");
}) ;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"doSomeThing3");
}) ;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新界面");
});
}
2018-02-28 17:26:07.752043+0800 GCD 系列知識(shí)點(diǎn)[15102:1596389] doSomeThing2
2018-02-28 17:26:07.752051+0800 GCD 系列知識(shí)點(diǎn)[15102:1596391] doSomeThing3
2018-02-28 17:26:07.761563+0800 GCD 系列知識(shí)點(diǎn)[15102:1596119] 刷新界面
2018-02-28 17:26:09.756480+0800 GCD 系列知識(shí)點(diǎn)[15102:1596390] doSomeThing1
會(huì)發(fā)現(xiàn)doSomeThing1
還沒(méi)執(zhí)行完就進(jìn)入dispatch_group_notify
打印刷新頁(yè)面苍姜,這是為什么了牢酵?
看代碼會(huì)發(fā)現(xiàn) doSomeThing1
是放在了一個(gè)異步線程中執(zhí)行的。但是dispatch_group_notify
只會(huì)監(jiān)聽(tīng)dispatch_group_async
中執(zhí)行的同步任務(wù)衙猪。如果dispatch_group_async
執(zhí)行的是個(gè)異步任務(wù)馍乙,那notify 監(jiān)聽(tīng)結(jié)果可能不是你想要的。
那我們能不能實(shí)現(xiàn)dispatch_group_async
執(zhí)行多異步任務(wù)垫释,同時(shí)還能監(jiān)聽(tīng)到任務(wù)全部完成了丝格?做到這些就需要dispatch_group_enter
和 dispatch_group_leave
了,看下面例子:
-(void)testGroup{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"doSomeThing1");
dispatch_group_leave(group);
});
}) ;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"doSomeThing2");
}) ;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"doSomeThing3");
}) ;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新界面");
});
}
2018-02-28 17:34:10.449511+0800 GCD 系列知識(shí)點(diǎn)[15199:1621095] doSomeThing3
2018-02-28 17:34:10.449512+0800 GCD 系列知識(shí)點(diǎn)[15199:1621093] doSomeThing2
2018-02-28 17:34:12.449752+0800 GCD 系列知識(shí)點(diǎn)[15199:1621094] doSomeThing1
2018-02-28 17:34:12.450328+0800 GCD 系列知識(shí)點(diǎn)[15199:1620866] 刷新界面
dispatch_group_enter 用來(lái)標(biāo)記這個(gè)任務(wù)的開(kāi)始棵譬,dispatch_group_leave用來(lái)標(biāo)記任務(wù)真正完成铁追。這樣成對(duì)標(biāo)記可以實(shí)現(xiàn)標(biāo)記dispatch_group_async
任務(wù)的真正執(zhí)行完畢,dispatch_group_notify
也就能監(jiān)聽(tīng)到了。
記酌4:
1. dispatch_group_enter 和 dispatch_group_leave 是成對(duì)出現(xiàn)的,不然dispatch_group 隊(duì)列會(huì)出錯(cuò)扭屁。
2. dispatch_group_enter調(diào)用n次算谈,就需要dispatch_group_leave調(diào)用n次,這樣dispatch_group_notify才會(huì)執(zhí)行
3. dispatch_group_enter/dispatch_group_leave 可以在mainQuene上用料滥,不會(huì)像Semaphore卡住主線程
5.1.3 用dispatch_group 處理多網(wǎng)絡(luò)請(qǐng)求都返回在刷新頁(yè)面
根據(jù)5.1.2 我們可以在實(shí)際開(kāi)發(fā)中運(yùn)用dispatch_group 來(lái)處理多個(gè)網(wǎng)絡(luò)請(qǐng)求都完成的通知然眼。因?yàn)槲覀兊木W(wǎng)絡(luò)請(qǐng)求都是異步的,正好可以用dispatch_group_enter 和 dispatch_group_leave
實(shí)現(xiàn)標(biāo)記完成葵腹。
下面寫(xiě)一段偽代碼展示:
dispatch_group_t group = dispatch_group_create();
//網(wǎng)絡(luò)請(qǐng)求1
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[異步網(wǎng)絡(luò)請(qǐng)求1:{
成功Block:dispatch_group_leave(group);
失敗Block:dispatch_group_leave(group);
}];
})
//網(wǎng)絡(luò)請(qǐng)求2
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[異步網(wǎng)絡(luò)請(qǐng)求3:{
成功Block:dispatch_group_leave(group);
失敗Block:dispatch_group_leave(group);
}];
})
//網(wǎng)絡(luò)請(qǐng)求3
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[異步網(wǎng)絡(luò)請(qǐng)求3:{
成功Block:dispatch_group_leave(group);
失敗Block:dispatch_group_leave(group);
}];
})
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有網(wǎng)絡(luò)請(qǐng)求完畢");
});
這樣就可以處理多網(wǎng)絡(luò)請(qǐng)求同時(shí)完成的情況了高每。
5.2 只執(zhí)行一次dispatch_once
我們?cè)趧?chuàng)建單例、或者有整個(gè)程序運(yùn)行過(guò)程中只執(zhí)行一次的代碼時(shí)践宴,我們就用到了 GCD 的 dispatch_once 函數(shù)鲸匿。使用
dispatch_once 函數(shù)能保證某段代碼在程序運(yùn)行過(guò)程中只被執(zhí)行1次,并且即使在多線程的環(huán)境下阻肩,dispatch_once也可以保證線程安全带欢。
- (void)test {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
});
}
5.3 延時(shí)執(zhí)行方法:dispatch_after
在指定時(shí)間(例如3秒)之后執(zhí)行某個(gè)任務(wù)】揪可以用 GCD 的dispatch_after函數(shù)來(lái)實(shí)現(xiàn)乔煞。
dispatch_after函數(shù)并不是在指定時(shí)間之后才開(kāi)始執(zhí)行處理,而是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中柒室。嚴(yán)格來(lái)說(shuō)渡贾,這個(gè)時(shí)間并不是絕對(duì)準(zhǔn)確的,但想要大致延遲執(zhí)行任務(wù)雄右,dispatch_after函數(shù)是很有效的
- (void)after {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2.0秒后異步追加任務(wù)代碼到主隊(duì)列空骚,并開(kāi)始執(zhí)行
NSLog(@"after---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
}
5.4 柵欄方法dispatch_barrier_async
我們有時(shí)需要異步執(zhí)行兩組操作纺讲,而且第一組操作執(zhí)行完之后,才能開(kāi)始執(zhí)行第二組操作府怯。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來(lái)刻诊,當(dāng)然這里的操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到dispatch_barrier_async方法在兩個(gè)操作組間形成柵欄牺丙。
如下则涯,如果想
-(void)testBarrier{
dispatch_queue_t dyConcurrent = dispatch_queue_create("dyConcurrent1", DISPATCH_QUEUE_CONCURRENT); //自己創(chuàng)建并行隊(duì)列
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dyConcurrent, ^{
for (int i = 0; i< 3; i++) {
NSLog(@"任務(wù)1");
}
});
dispatch_group_async(group, dyConcurrent, ^{
for (int i = 0; i< 3; i++) {
NSLog(@"任務(wù)2");
}
});
dispatch_barrier_async(dyConcurrent, ^{
NSLog(@"追加追加追加");
});
dispatch_group_async(group, dyConcurrent, ^{
for (int i = 0; i< 3; i++) {
NSLog(@"任務(wù)3");
}
});
dispatch_group_async(group, dyConcurrent, ^{
for (int i = 0; i< 3; i++) {
NSLog(@"任務(wù)4");
}
});
}
2018-03-01 09:16:35.722991+0800 GCD 系列知識(shí)點(diǎn)[16166:1788483] 打印當(dāng)前線程---<NSThread: 0x60400007f880>{number = 1, name = main}
2018-03-01 09:16:35.723276+0800 GCD 系列知識(shí)點(diǎn)[16166:1788561] 任務(wù)1
2018-03-01 09:16:35.723288+0800 GCD 系列知識(shí)點(diǎn)[16166:1788563] 任務(wù)2
2018-03-01 09:16:35.723442+0800 GCD 系列知識(shí)點(diǎn)[16166:1788561] 任務(wù)1
2018-03-01 09:16:35.723452+0800 GCD 系列知識(shí)點(diǎn)[16166:1788563] 任務(wù)2
2018-03-01 09:16:35.723532+0800 GCD 系列知識(shí)點(diǎn)[16166:1788561] 任務(wù)1
2018-03-01 09:16:35.723549+0800 GCD 系列知識(shí)點(diǎn)[16166:1788563] 任務(wù)2
2018-03-01 09:16:35.723785+0800 GCD 系列知識(shí)點(diǎn)[16166:1788563] 追加追加追加
2018-03-01 09:16:35.724089+0800 GCD 系列知識(shí)點(diǎn)[16166:1788560] 任務(wù)3
2018-03-01 09:16:35.724357+0800 GCD 系列知識(shí)點(diǎn)[16166:1788563] 任務(wù)4
2018-03-01 09:16:35.725130+0800 GCD 系列知識(shí)點(diǎn)[16166:1788560] 任務(wù)3
2018-03-01 09:16:35.725511+0800 GCD 系列知識(shí)點(diǎn)[16166:1788563] 任務(wù)4
2018-03-01 09:16:35.725511+0800 GCD 系列知識(shí)點(diǎn)[16166:1788560] 任務(wù)3
2018-03-01 09:16:35.725900+0800 GCD 系列知識(shí)點(diǎn)[16166:1788563] 任務(wù)4
會(huì)發(fā)現(xiàn),任務(wù)1和任務(wù)2 全部執(zhí)行完畢后 在執(zhí)行追加任務(wù)冲簿,然后再執(zhí)行任務(wù)3和任務(wù)4粟判,如果我們下面代碼注釋掉再運(yùn)行一下
/*
dispatch_barrier_async(dyConcurrent, ^{
NSLog(@"追加追加追加");
});
*/
2018-03-01 09:19:36.735062+0800 GCD 系列知識(shí)點(diǎn)[16214:1797692] 任務(wù)1
2018-03-01 09:19:36.735062+0800 GCD 系列知識(shí)點(diǎn)[16214:1797691] 任務(wù)2
2018-03-01 09:19:36.735089+0800 GCD 系列知識(shí)點(diǎn)[16214:1797705] 任務(wù)3
2018-03-01 09:19:36.735116+0800 GCD 系列知識(shí)點(diǎn)[16214:1797693] 任務(wù)4
2018-03-01 09:19:36.735310+0800 GCD 系列知識(shí)點(diǎn)[16214:1797691] 任務(wù)2
2018-03-01 09:19:36.735315+0800 GCD 系列知識(shí)點(diǎn)[16214:1797692] 任務(wù)1
2018-03-01 09:19:36.735436+0800 GCD 系列知識(shí)點(diǎn)[16214:1797705] 任務(wù)3
2018-03-01 09:19:36.735623+0800 GCD 系列知識(shí)點(diǎn)[16214:1797693] 任務(wù)4
2018-03-01 09:19:36.735653+0800 GCD 系列知識(shí)點(diǎn)[16214:1797691] 任務(wù)2
2018-03-01 09:19:36.735716+0800 GCD 系列知識(shí)點(diǎn)[16214:1797692] 任務(wù)1
2018-03-01 09:19:36.736728+0800 GCD 系列知識(shí)點(diǎn)[16214:1797705] 任務(wù)3
2018-03-01 09:19:36.738247+0800 GCD 系列知識(shí)點(diǎn)[16214:1797693] 任務(wù)4
會(huì)發(fā)現(xiàn)各個(gè)任務(wù)是混合異步執(zhí)行的。
通過(guò)這個(gè)例子我們能明白dispatch_barrier_async主要是分割異步任務(wù)的作用峦剔。
5.5 dispatch_apply 快速遍歷
//參數(shù)iterations遍歷次數(shù)档礁,queue執(zhí)行遍歷任務(wù)的隊(duì)列,block每次遍歷回調(diào)
dispatch_apply(size_t iterations, dispatch_queue_t queue,
DISPATCH_NOESCAPE void (^block)(size_t));
和一般我們用for循環(huán)遍歷不同的是吝沫,如果dispatch_apply
調(diào)用的時(shí)候傳入queue
的是并發(fā)隊(duì)列呻澜,那么它的遍歷就是異步執(zhí)行的,會(huì)在多個(gè)線程中進(jìn)行惨险,而我們直接for循環(huán)都是在當(dāng)前線程中進(jìn)行的羹幸。如下
-(void)testApply{
NSArray *imageArray = @[@"image1",@"image2",@"image3",@"image4",@"image5",@"image6",@"image7",@"image8"];
// dispatch_queue_t queue = dispatch_queue_create("new dySerial", DISPATCH_QUEUE_SERIAL); //自己創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(imageArray.count, queue, ^(size_t index) {
NSLog(@"%@---%@",imageArray[index], [NSThread currentThread]);
});
}
018-03-01 09:49:13.824457+0800 GCD 系列知識(shí)點(diǎn)[16580:1890506] image1---<NSThread: 0x600000064e80>{number = 1, name = main}
2018-03-01 09:49:13.824554+0800 GCD 系列知識(shí)點(diǎn)[16580:1890719] image3---<NSThread: 0x60400047be00>{number = 5, name = (null)}
2018-03-01 09:49:13.824555+0800 GCD 系列知識(shí)點(diǎn)[16580:1890718] image2---<NSThread: 0x600000265780>{number = 4, name = (null)}
2018-03-01 09:49:13.824601+0800 GCD 系列知識(shí)點(diǎn)[16580:1890721] image4---<NSThread: 0x6040004647c0>{number = 3, name = (null)}
2018-03-01 09:49:13.824706+0800 GCD 系列知識(shí)點(diǎn)[16580:1890506] image5---<NSThread: 0x600000064e80>{number = 1, name = main}
2018-03-01 09:49:13.824805+0800 GCD 系列知識(shí)點(diǎn)[16580:1890719] image6---<NSThread: 0x60400047be00>{number = 5, name = (null)}
2018-03-01 09:49:13.824987+0800 GCD 系列知識(shí)點(diǎn)[16580:1890721] image8---<NSThread: 0x6040004647c0>{number = 3, name = (null)}
2018-03-01 09:49:13.824991+0800 GCD 系列知識(shí)點(diǎn)[16580:1890718] image7---<NSThread: 0x600000265780>{number = 4, name = (null)}
會(huì)發(fā)現(xiàn)打印的線程號(hào)都是不同的。但是如果傳入的是個(gè)串行隊(duì)列(非主隊(duì)列)它就和普通的for循環(huán)沒(méi)什么不同了辫愉。如下
-(void)testApply{
NSArray *imageArray = @[@"image1",@"image2",@"image3",@"image4",@"image5",@"image6",@"image7",@"image8"];
dispatch_queue_t dySerial = dispatch_queue_create("new dySerial", DISPATCH_QUEUE_SERIAL); //自己創(chuàng)建串行隊(duì)列
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(imageArray.count, dySerial, ^(size_t index) {
NSLog(@"%@---%@",imageArray[index], [NSThread currentThread]);
});
}
2018-03-01 09:42:20.361535+0800 GCD 系列知識(shí)點(diǎn)[16487:1861258] image1---<NSThread: 0x60000006e140>{number = 1, name = main}
2018-03-01 09:42:20.361714+0800 GCD 系列知識(shí)點(diǎn)[16487:1861258] image2---<NSThread: 0x60000006e140>{number = 1, name = main}
2018-03-01 09:42:20.361844+0800 GCD 系列知識(shí)點(diǎn)[16487:1861258] image3---<NSThread: 0x60000006e140>{number = 1, name = main}
2018-03-01 09:42:20.362145+0800 GCD 系列知識(shí)點(diǎn)[16487:1861258] image4---<NSThread: 0x60000006e140>{number = 1, name = main}
2018-03-01 09:42:20.362262+0800 GCD 系列知識(shí)點(diǎn)[16487:1861258] image5---<NSThread: 0x60000006e140>{number = 1, name = main}
2018-03-01 09:42:20.362374+0800 GCD 系列知識(shí)點(diǎn)[16487:1861258] image6---<NSThread: 0x60000006e140>{number = 1, name = main}
2018-03-01 09:42:20.362489+0800 GCD 系列知識(shí)點(diǎn)[16487:1861258] image7---<NSThread: 0x60000006e140>{number = 1, name = main}
2018-03-01 09:42:20.362604+0800 GCD 系列知識(shí)點(diǎn)[16487:1861258] image8---<NSThread: 0x60000006e140>{number = 1, name = main}
會(huì)發(fā)現(xiàn)和不同的for循環(huán)沒(méi)什么不同栅受,都是在主線程中進(jìn)行的。
注意:如果執(zhí)行
dispatch_apply
的線程是串行隊(duì)列A的線程恭朗,同時(shí)執(zhí)行dispatch_apply
時(shí)傳入queue
的也是串行隊(duì)列A屏镊,會(huì)發(fā)生死鎖。比如直接在主隊(duì)列中執(zhí)行dispatch_apply
痰腮,同時(shí)傳入queue
也是主隊(duì)列而芥,如:
//直接調(diào)用會(huì)出現(xiàn)死鎖
[self testApply];
-(void)testApply{
NSArray *imageArray = @[@"image1",@"image2",@"image3",@"image4",@"image5",@"image6",@"image7",@"image8"];
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_apply(imageArray.count, queue, ^(size_t index) {
NSLog(@"%@---%@",imageArray[index], [NSThread currentThread]);
});
}
當(dāng)然這樣做也沒(méi)什么意義膀值,一般
dispatch_apply
都是為了高效的異步的遍歷數(shù)組蔚出。所以一般dispatch_apply
傳入的queue
都是一個(gè)并發(fā)隊(duì)列,同時(shí)還把dispatch_apply
整體方法一個(gè)異步執(zhí)行的并發(fā)隊(duì)列中虫腋,如:
NSArray *array = @[@"1", @"2", @"3", @"4", @"5", @"6"];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
dispatch_apply([array count], queue, ^(size_t index) {
NSLog(@"%zu : %@", index, [array objectAtIndex:index]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"currentThread = %@", [NSThread currentThread]);
NSLog(@"done");
});
});
5.6 信號(hào)量 dispatch_semaphore
干啥用的了骄酗,總結(jié)一句話:使用dispatch_semaphore 可以更好的控制并發(fā)多線程的任務(wù)處理。
//創(chuàng)建一個(gè)信號(hào)量悦冀,給他設(shè)置個(gè)信號(hào)量值
dispatch_semaphore_create(long value);
//可以使總信號(hào)量減1趋翻,當(dāng)信號(hào)總量為0時(shí)就會(huì)一直等待,阻塞當(dāng)前線程(當(dāng)timeout 時(shí)間過(guò)后 也可以繼續(xù)執(zhí)行)盒蟆,否則就可以正常執(zhí)行踏烙。
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
//發(fā)送一個(gè)信號(hào)师骗,使信號(hào)總量加1
dispatch_semaphore_signal(dispatch_semaphore_t dsema);
看著也看不出來(lái)有啥用,下面舉例一些常見(jiàn)用法讨惩。
5.6.1 控制并發(fā)線程數(shù)量
下面來(lái)看看實(shí)際應(yīng)用的例子:
現(xiàn)在我有一組圖片地址辟癌,我需要開(kāi)辟一個(gè)并發(fā)隊(duì)列來(lái)下載這些圖片,等這些圖片都下載完了荐捻,告訴我我要去搞事情黍少。
- (void)testSemaphoreSomething{
NSArray *imageArray = @[@"image1",@"image2",@"image3",@"image4",@"image5",@"image6",@"image7",@"image8",@"image8",@"image10"];
dispatch_queue_t dyConcurrent = dispatch_queue_create("dyConcurrent1", DISPATCH_QUEUE_CONCURRENT); //自己創(chuàng)建并行隊(duì)列
// dispatch_semaphore_t cacheSemphore = dispatch_semaphore_create(5);
for (NSString *oneImage in imageArray){
dispatch_async(dyConcurrent, ^{
// dispatch_semaphore_wait(cacheSemphore, DISPATCH_TIME_FOREVER);
NSLog(@"下載圖片:%@ 線程:%@",oneImage,[NSThread currentThread]);
sleep(1);
NSLog(@"下載完成");
// dispatch_semaphore_signal(cacheSemphore);
});
}
}
我們直接運(yùn)行會(huì)發(fā)現(xiàn)會(huì)一下開(kāi)辟imageArray.count
個(gè)線程來(lái)處理任務(wù)。
2018-02-27 18:22:11.014569+0800 GCD 系列知識(shí)點(diǎn)[1237:286459] 打印當(dāng)前線程---<NSThread: 0x604000070200>{number = 1, name = main}
2018-02-27 18:22:11.014969+0800 GCD 系列知識(shí)點(diǎn)[1237:286648] 下載圖片:image5 線程:<NSThread: 0x604000272240>{number = 7, name = (null)}
2018-02-27 18:22:11.014974+0800 GCD 系列知識(shí)點(diǎn)[1237:286651] 下載圖片:image2 線程:<NSThread: 0x604000272140>{number = 4, name = (null)}
2018-02-27 18:22:11.015007+0800 GCD 系列知識(shí)點(diǎn)[1237:286647] 下載圖片:image4 線程:<NSThread: 0x604000272200>{number = 6, name = (null)}
2018-02-27 18:22:11.015007+0800 GCD 系列知識(shí)點(diǎn)[1237:286650] 下載圖片:image3 線程:<NSThread: 0x600000279080>{number = 5, name = (null)}
2018-02-27 18:22:11.015035+0800 GCD 系列知識(shí)點(diǎn)[1237:286649] 下載圖片:image1 線程:<NSThread: 0x604000272100>{number = 3, name = (null)}
2018-02-27 18:22:11.015389+0800 GCD 系列知識(shí)點(diǎn)[1237:286668] 下載圖片:image6 線程:<NSThread: 0x600000279100>{number = 8, name = (null)}
2018-02-27 18:22:11.015492+0800 GCD 系列知識(shí)點(diǎn)[1237:286669] 下載圖片:image7 線程:<NSThread: 0x600000279180>{number = 9, name = (null)}
2018-02-27 18:22:11.015589+0800 GCD 系列知識(shí)點(diǎn)[1237:286670] 下載圖片:image8 線程:<NSThread: 0x600000279200>{number = 10, name = (null)}
2018-02-27 18:22:11.015684+0800 GCD 系列知識(shí)點(diǎn)[1237:286671] 下載圖片:image9 線程:<NSThread: 0x600000279280>{number = 11, name = (null)}
2018-02-27 18:22:11.015764+0800 GCD 系列知識(shí)點(diǎn)[1237:286672] 下載圖片:image10 線程:<NSThread: 0x600000279300>{number = 12, name = (null)}
2018-02-27 18:22:12.017230+0800 GCD 系列知識(shí)點(diǎn)[1237:286651] 下載完成
2018-02-27 18:22:12.017232+0800 GCD 系列知識(shí)點(diǎn)[1237:286647] 下載完成
2018-02-27 18:22:12.017249+0800 GCD 系列知識(shí)點(diǎn)[1237:286648] 下載完成
2018-02-27 18:22:12.017253+0800 GCD 系列知識(shí)點(diǎn)[1237:286650] 下載完成
2018-02-27 18:22:12.017292+0800 GCD 系列知識(shí)點(diǎn)[1237:286649] 下載完成
2018-02-27 18:22:12.018566+0800 GCD 系列知識(shí)點(diǎn)[1237:286668] 下載完成
2018-02-27 18:22:12.023732+0800 GCD 系列知識(shí)點(diǎn)[1237:286671] 下載完成
2018-02-27 18:22:12.023728+0800 GCD 系列知識(shí)點(diǎn)[1237:286670] 下載完成
2018-02-27 18:22:12.023732+0800 GCD 系列知識(shí)點(diǎn)[1237:286669] 下載完成
2018-02-27 18:22:12.023761+0800 GCD 系列知識(shí)點(diǎn)[1237:286672] 下載完成
假如這里imageArray.count
是100多处面,很顯然厂置,我們這樣的暴力處理是不切合實(shí)際的。使用 Semaphore我們可以控制同時(shí)并發(fā)的線程數(shù)量魂角。我們打開(kāi)屏蔽代碼昵济,看輸出結(jié)果:
2018-02-27 18:25:25.741325+0800 GCD 系列知識(shí)點(diǎn)[1265:295325] 打印當(dāng)前線程---<NSThread: 0x60400006e800>{number = 1, name = main}
2018-02-27 18:25:25.741625+0800 GCD 系列知識(shí)點(diǎn)[1265:295509] 下載圖片:image2 線程:<NSThread: 0x60000027b380>{number = 4, name = (null)}
2018-02-27 18:25:25.741630+0800 GCD 系列知識(shí)點(diǎn)[1265:295508] 下載圖片:image1 線程:<NSThread: 0x60400026fac0>{number = 3, name = (null)}
2018-02-27 18:25:25.741637+0800 GCD 系列知識(shí)點(diǎn)[1265:295510] 下載圖片:image3 線程:<NSThread: 0x60000027b280>{number = 5, name = (null)}
2018-02-27 18:25:25.741677+0800 GCD 系列知識(shí)點(diǎn)[1265:295511] 下載圖片:image4 線程:<NSThread: 0x60000027b240>{number = 6, name = (null)}
2018-02-27 18:25:25.742019+0800 GCD 系列知識(shí)點(diǎn)[1265:295525] 下載圖片:image6 線程:<NSThread: 0x60400026fd00>{number = 7, name = (null)}
2018-02-27 18:25:26.742404+0800 GCD 系列知識(shí)點(diǎn)[1265:295509] 下載完成
2018-02-27 18:25:26.742442+0800 GCD 系列知識(shí)點(diǎn)[1265:295508] 下載完成
2018-02-27 18:25:26.742871+0800 GCD 系列知識(shí)點(diǎn)[1265:295525] 下載完成
2018-02-27 18:25:26.742871+0800 GCD 系列知識(shí)點(diǎn)[1265:295511] 下載完成
2018-02-27 18:25:26.742907+0800 GCD 系列知識(shí)點(diǎn)[1265:295510] 下載完成
2018-02-27 18:25:26.742965+0800 GCD 系列知識(shí)點(diǎn)[1265:295524] 下載圖片:image5 線程:<NSThread: 0x604000270080>{number = 8, name = (null)}
2018-02-27 18:25:26.743013+0800 GCD 系列知識(shí)點(diǎn)[1265:295526] 下載圖片:image7 線程:<NSThread: 0x60000027c080>{number = 9, name = (null)}
2018-02-27 18:25:26.743472+0800 GCD 系列知識(shí)點(diǎn)[1265:295527] 下載圖片:image8 線程:<NSThread: 0x60000027c700>{number = 10, name = (null)}
2018-02-27 18:25:26.743472+0800 GCD 系列知識(shí)點(diǎn)[1265:295528] 下載圖片:image9 線程:<NSThread: 0x60000027c640>{number = 11, name = (null)}
2018-02-27 18:25:26.743546+0800 GCD 系列知識(shí)點(diǎn)[1265:295529] 下載圖片:image10 線程:<NSThread: 0x60400026ac80>{number = 12, name = (null)}
2018-02-27 18:25:27.746854+0800 GCD 系列知識(shí)點(diǎn)[1265:295528] 下載完成
2018-02-27 18:25:27.746912+0800 GCD 系列知識(shí)點(diǎn)[1265:295527] 下載完成
2018-02-27 18:25:27.746855+0800 GCD 系列知識(shí)點(diǎn)[1265:295524] 下載完成
2018-02-27 18:25:27.746954+0800 GCD 系列知識(shí)點(diǎn)[1265:295526] 下載完成
2018-02-27 18:25:27.746981+0800 GCD 系列知識(shí)點(diǎn)[1265:295529] 下載完成
會(huì)發(fā)現(xiàn)我們先開(kāi)辟 5 個(gè)線程去下載,5個(gè)都下載完成了后再開(kāi)辟5個(gè)去搞野揪,這樣就可以減輕系統(tǒng)并發(fā)的數(shù)量访忿。
看結(jié)果你是不是感覺(jué)這有點(diǎn)像dispatch_barrier_async
都是把任務(wù)拆成幾部分一塊一塊執(zhí)行,但是他們是有本質(zhì)不同的:dispatch_barrier_async
控制的是任務(wù)斯稳,它操作的對(duì)象是任務(wù)分塊醉顽,不會(huì)處理線程的多少,而Semaphore控制同時(shí)并發(fā)的線程數(shù)量平挑,再由一定的線程數(shù)量去執(zhí)行任務(wù)。這好比同樣是蓋樓要10個(gè)人系草,barrier
類似于我把樓分成2部分通熄,我們10個(gè)人先蓋第一部分,第一部分完了再蓋第二部分找都。而Semaphore
是我們同時(shí)只讓5個(gè)人蓋樓唇辨,剩下的休息,誰(shuí)干完了誰(shuí)休息能耻,那個(gè)之前在休息的上來(lái)干赏枚。
那如果dispatch_semaphore_create(5)
改成dispatch_semaphore_create(1)
會(huì)怎么樣,改完之后運(yùn)行晓猛。
2018-02-27 18:29:33.880314+0800 GCD 系列知識(shí)點(diǎn)[1322:310027] 打印當(dāng)前線程---<NSThread: 0x604000077200>{number = 1, name = main}
2018-02-27 18:29:33.880623+0800 GCD 系列知識(shí)點(diǎn)[1322:310152] 下載圖片:image1 線程:<NSThread: 0x604000478c40>{number = 3, name = (null)}
2018-02-27 18:29:34.881307+0800 GCD 系列知識(shí)點(diǎn)[1322:310152] 下載完成
2018-02-27 18:29:34.881755+0800 GCD 系列知識(shí)點(diǎn)[1322:310155] 下載圖片:image3 線程:<NSThread: 0x60400046f280>{number = 4, name = (null)}
2018-02-27 18:29:35.887236+0800 GCD 系列知識(shí)點(diǎn)[1322:310155] 下載完成
2018-02-27 18:29:35.887694+0800 GCD 系列知識(shí)點(diǎn)[1322:310153] 下載圖片:image2 線程:<NSThread: 0x604000479540>{number = 5, name = (null)}
2018-02-27 18:29:36.891046+0800 GCD 系列知識(shí)點(diǎn)[1322:310153] 下載完成
2018-02-27 18:29:36.891503+0800 GCD 系列知識(shí)點(diǎn)[1322:310154] 下載圖片:image4 線程:<NSThread: 0x60000007a380>{number = 6, name = (null)}
2018-02-27 18:29:37.894212+0800 GCD 系列知識(shí)點(diǎn)[1322:310154] 下載完成
2018-02-27 18:29:37.894732+0800 GCD 系列知識(shí)點(diǎn)[1322:310164] 下載圖片:image5 線程:<NSThread: 0x600000263280>{number = 7, name = (null)}
2018-02-27 18:29:38.900181+0800 GCD 系列知識(shí)點(diǎn)[1322:310164] 下載完成
2018-02-27 18:29:38.900712+0800 GCD 系列知識(shí)點(diǎn)[1322:310166] 下載圖片:image7 線程:<NSThread: 0x604000479740>{number = 8, name = (null)}
2018-02-27 18:29:39.904618+0800 GCD 系列知識(shí)點(diǎn)[1322:310166] 下載完成
2018-02-27 18:29:39.905013+0800 GCD 系列知識(shí)點(diǎn)[1322:310165] 下載圖片:image6 線程:<NSThread: 0x600000263580>{number = 9, name = (null)}
2018-02-27 18:29:40.908401+0800 GCD 系列知識(shí)點(diǎn)[1322:310165] 下載完成
2018-02-27 18:29:40.908814+0800 GCD 系列知識(shí)點(diǎn)[1322:310168] 下載圖片:image9 線程:<NSThread: 0x6000002631c0>{number = 10, name = (null)}
2018-02-27 18:29:41.910161+0800 GCD 系列知識(shí)點(diǎn)[1322:310168] 下載完成
2018-02-27 18:29:41.910634+0800 GCD 系列知識(shí)點(diǎn)[1322:310167] 下載圖片:image8 線程:<NSThread: 0x6040004794c0>{number = 11, name = (null)}
2018-02-27 18:29:42.915150+0800 GCD 系列知識(shí)點(diǎn)[1322:310167] 下載完成
2018-02-27 18:29:42.915664+0800 GCD 系列知識(shí)點(diǎn)[1322:310169] 下載圖片:image10 線程:<NSThread: 0x604000471980>{number = 12, name = (null)}
2018-02-27 18:29:43.919164+0800 GCD 系列知識(shí)點(diǎn)[1322:310169] 下載完成
會(huì)發(fā)現(xiàn)調(diào)用的是并發(fā)隊(duì)列饿幅,但是限制信號(hào)量總數(shù)為1 后,并發(fā)隊(duì)列就變成了串行隊(duì)列戒职。
5.6.2 對(duì)多線程并發(fā)執(zhí)行任務(wù)共享資源進(jìn)行加鎖處理
案例:總共有20張火車票栗恩,有兩個(gè)售賣(mài)火車票的窗口,一個(gè)是北京火車票售賣(mài)窗口洪燥,另一個(gè)是上嚎某樱火車票售賣(mài)窗口乳乌。兩個(gè)窗口同時(shí)售賣(mài)火車票,賣(mài)完為止市咆。
這里的問(wèn)題核心是汉操,兩個(gè)窗口同時(shí)買(mǎi)票,在賣(mài)出一張票后總票數(shù)都會(huì)減1蒙兰,如何確保票不多賣(mài)或賣(mài)叉磷瘤?這就相當(dāng)于計(jì)算機(jī)中的多條線程同時(shí)寫(xiě)一個(gè)資源,可能會(huì)出現(xiàn)混亂的問(wèn)題癞己“蛘看Demo
-(void)testSaleTicketSemaphoreLock{
self.allTicket = 20;
//開(kāi)辟兩個(gè)線程來(lái)買(mǎi)票
NSThread *oneThread = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
oneThread.name = @"北京";
NSThread *twoThread = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
twoThread.name = @"上海";
[oneThread start];
[twoThread start];
}
-(void)saleTicket{
while (1) {
if (self.allTicket > 0){
self.allTicket --;
NSLog(@"剩余票數(shù):%ld 當(dāng)前窗口線程:%@",(long)self.allTicket,[NSThread currentThread].name);
[NSThread sleepForTimeInterval:1];
}else{
NSLog(@"所有票都買(mǎi)完了");
break;
}
}
}
2018-02-28 11:05:45.911240+0800 GCD 系列知識(shí)點(diǎn)[11745:1027862] 打印當(dāng)前線程---<NSThread: 0x600000068140>{number = 1, name = main}
2018-02-28 11:05:45.911688+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):19 當(dāng)前窗口線程:北京
2018-02-28 11:05:45.911697+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):18 當(dāng)前窗口線程:上海
2018-02-28 11:05:46.916487+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):17 當(dāng)前窗口線程:上海
2018-02-28 11:05:46.916492+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):16 當(dāng)前窗口線程:北京
2018-02-28 11:05:47.922052+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):15 當(dāng)前窗口線程:北京
2018-02-28 11:05:47.922066+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):14 當(dāng)前窗口線程:上海
2018-02-28 11:05:48.925762+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):13 當(dāng)前窗口線程:北京
2018-02-28 11:05:48.925762+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):12 當(dāng)前窗口線程:上海
2018-02-28 11:05:49.930191+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):11 當(dāng)前窗口線程:上海
2018-02-28 11:05:49.930229+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):11 當(dāng)前窗口線程:北京
2018-02-28 11:05:50.934367+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):10 當(dāng)前窗口線程:北京
2018-02-28 11:05:50.934473+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):9 當(dāng)前窗口線程:上海
2018-02-28 11:05:51.937956+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):8 當(dāng)前窗口線程:北京
2018-02-28 11:05:51.937956+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):8 當(dāng)前窗口線程:上海
2018-02-28 11:05:52.942655+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):7 當(dāng)前窗口線程:上海
2018-02-28 11:05:52.942655+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):7 當(dāng)前窗口線程:北京
2018-02-28 11:05:53.945901+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):6 當(dāng)前窗口線程:上海
2018-02-28 11:05:53.945901+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):6 當(dāng)前窗口線程:北京
2018-02-28 11:05:54.949424+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):5 當(dāng)前窗口線程:北京
2018-02-28 11:05:54.949435+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):4 當(dāng)前窗口線程:上海
2018-02-28 11:05:55.952903+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):3 當(dāng)前窗口線程:北京
2018-02-28 11:05:55.952904+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):2 當(dāng)前窗口線程:上海
2018-02-28 11:05:56.956317+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 剩余票數(shù):1 當(dāng)前窗口線程:上海
2018-02-28 11:05:56.956321+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 剩余票數(shù):0 當(dāng)前窗口線程:北京
2018-02-28 11:05:57.961572+0800 GCD 系列知識(shí)點(diǎn)[11745:1028003] 所有票都買(mǎi)完了
2018-02-28 11:05:57.961599+0800 GCD 系列知識(shí)點(diǎn)[11745:1028004] 所有票都買(mǎi)完了
觀察會(huì)發(fā)現(xiàn)余票在 11、8 痹雅、7仰担、6的時(shí)候揣想那混亂,多賣(mài)了票绩社。這就是多線程訪問(wèn)公共資源出現(xiàn)不同步的問(wèn)題摔蓝。我們使用dispatch_semaphore 來(lái)加鎖處理一下,看看結(jié)果怎么樣愉耙?
-(void)testSaleTicketSemaphoreLock{
self.allTicket = 20;
self.ticketSemaphore = dispatch_semaphore_create(1);
//開(kāi)辟兩個(gè)線程來(lái)買(mǎi)票
NSThread *oneThread = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
oneThread.name = @"北京";
NSThread *twoThread = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
twoThread.name = @"上海";
[oneThread start];
[twoThread start];
}
-(void)saleTicket{
while (1) {
//加鎖 如果信號(hào)量>0 執(zhí)行wait 信號(hào)量減1 下方代碼執(zhí)行贮尉,如果信號(hào)量<0 那就不執(zhí)行下方代碼 阻塞當(dāng)前
dispatch_semaphore_wait(self.ticketSemaphore, DISPATCH_TIME_FOREVER);
if (self.allTicket > 0){
self.allTicket --;
NSLog(@"剩余票數(shù):%ld 當(dāng)前窗口線程:%@",(long)self.allTicket,[NSThread currentThread].name);
[NSThread sleepForTimeInterval:1];
}else{
NSLog(@"所有票都買(mǎi)完了");
dispatch_semaphore_signal(self.ticketSemaphore);
break;
}
//解鎖 執(zhí)行signal 信號(hào)量+1
dispatch_semaphore_signal(self.ticketSemaphore);
}
}
2018-02-28 14:17:28.118236+0800 GCD 系列知識(shí)點(diǎn)[13342:1195687] 打印當(dāng)前線程---<NSThread: 0x604000068fc0>{number = 1, name = main}
2018-02-28 14:17:28.120479+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):19 當(dāng)前窗口線程:北京
2018-02-28 14:17:29.124787+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):18 當(dāng)前窗口線程:上海
2018-02-28 14:17:30.127421+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):17 當(dāng)前窗口線程:北京
2018-02-28 14:17:31.130508+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):16 當(dāng)前窗口線程:上海
2018-02-28 14:17:32.133827+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):15 當(dāng)前窗口線程:北京
2018-02-28 14:17:33.138644+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):14 當(dāng)前窗口線程:上海
2018-02-28 14:17:34.142570+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):13 當(dāng)前窗口線程:北京
2018-02-28 14:17:35.143104+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):12 當(dāng)前窗口線程:上海
2018-02-28 14:17:36.144129+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):11 當(dāng)前窗口線程:北京
2018-02-28 14:17:37.149313+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):10 當(dāng)前窗口線程:上海
2018-02-28 14:17:38.154850+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):9 當(dāng)前窗口線程:北京
2018-02-28 14:17:39.160380+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):8 當(dāng)前窗口線程:上海
2018-02-28 14:17:40.165849+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):7 當(dāng)前窗口線程:北京
2018-02-28 14:17:41.167534+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):6 當(dāng)前窗口線程:上海
2018-02-28 14:17:42.173125+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):5 當(dāng)前窗口線程:北京
2018-02-28 14:17:43.177330+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):4 當(dāng)前窗口線程:上海
2018-02-28 14:17:44.181994+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):3 當(dāng)前窗口線程:北京
2018-02-28 14:17:45.183557+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):2 當(dāng)前窗口線程:上海
2018-02-28 14:17:46.189193+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 剩余票數(shù):1 當(dāng)前窗口線程:北京
2018-02-28 14:17:47.194826+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 剩余票數(shù):0 當(dāng)前窗口線程:上海
2018-02-28 14:17:48.195913+0800 GCD 系列知識(shí)點(diǎn)[13342:1195906] 所有票都買(mǎi)完了
2018-02-28 14:17:48.196297+0800 GCD 系列知識(shí)點(diǎn)[13342:1195907] 所有票都買(mǎi)完了
會(huì)發(fā)現(xiàn)總剩余票數(shù)沒(méi)有發(fā)生錯(cuò)位。其實(shí)dispatch_semaphore這里就是控制了多線程處理任務(wù) 并發(fā)時(shí)機(jī)朴沿,也是5.1.1的一種特殊形式猜谚。
5.6.3 頁(yè)面有多個(gè)網(wǎng)絡(luò)請(qǐng)求都完成后才處理任務(wù)
上面說(shuō)的使用dispatch_group_enter
和dispatch_group_leave
可以實(shí)現(xiàn)多網(wǎng)絡(luò)請(qǐng)求完成監(jiān)聽(tīng)處理。我們使用dispatch_semaphore也可以完成這種操作赌渣,具體直接上下面那段偽代碼
dispatch_group_t group = dispatch_group_create();
//網(wǎng)絡(luò)請(qǐng)求1
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//創(chuàng)建一個(gè)為0信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[異步網(wǎng)絡(luò)請(qǐng)求1:{
//成功或失敗給信號(hào)加1
成功Block:dispatch_semaphore_signal(semaphore);
失敗Block:dispatch_semaphore_signal(semaphore)魏铅;
}];
//只有信號(hào)量>1 下面代碼才走,方法才算執(zhí)行完畢
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
})坚芜;
//網(wǎng)絡(luò)請(qǐng)求2
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//創(chuàng)建一個(gè)為0信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[異步網(wǎng)絡(luò)請(qǐng)求2:{
//成功或失敗給信號(hào)加1
成功Block:dispatch_semaphore_signal(semaphore);
失敗Block:dispatch_semaphore_signal(semaphore)览芳;
}];
//只有信號(hào)量>1 下面代碼才走,方法才算執(zhí)行完畢
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
})鸿竖;
//網(wǎng)絡(luò)請(qǐng)求3
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//創(chuàng)建一個(gè)為0信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[異步網(wǎng)絡(luò)請(qǐng)求3:{
//成功或失敗給信號(hào)加1
成功Block:dispatch_semaphore_signal(semaphore);
失敗Block:dispatch_semaphore_signal(semaphore)沧竟;
}];
//只有信號(hào)量>1 下面代碼才走,方法才算執(zhí)行完畢
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
})缚忧;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有網(wǎng)絡(luò)請(qǐng)求完畢");
})
這樣的組合就能實(shí)現(xiàn)多網(wǎng)絡(luò)請(qǐng)求都有響應(yīng)后的網(wǎng)絡(luò)回調(diào)了悟泵!
補(bǔ)充: 那么如何讓這些網(wǎng)絡(luò)請(qǐng)求有個(gè)先后順序了,就是說(shuō)請(qǐng)求1返回回來(lái)的參數(shù)我要用到請(qǐng)求2上闪水,這樣的需求也很常見(jiàn)魁袜,用到的知識(shí)就不是GCD的了,而是NSOperation上的知識(shí)了,從別人博客弄來(lái)的代碼峰弹,直接放上:
//按照順序
NSBlockOperation *operation_1 = [NSBlockOperation blockOperationWithBlock:^{
[self request1];
}];
NSBlockOperation *operation_2 = [NSBlockOperation blockOperationWithBlock:^{
[self request2];
}];
NSBlockOperation *operation_3 = [NSBlockOperation blockOperationWithBlock:^{
[self request3];
}];
//設(shè)置依賴
[operation_2 addDependency:operation_1];
[operation_3 addDependency:operation_1];
//創(chuàng)建隊(duì)列并添加任務(wù)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperations:@[operation_3,operation_2,operation_1] waitUntilFinished:YES];
其中的[self request]
里面代碼還是使用信號(hào)量來(lái)控制網(wǎng)絡(luò)請(qǐng)求真正結(jié)束店量,拿request1代碼舉個(gè)栗子:
-(void)request1{
//創(chuàng)建一個(gè)為0信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[異步網(wǎng)絡(luò)請(qǐng)求3:{
//成功或失敗給信號(hào)加1
成功Block:dispatch_semaphore_signal(semaphore);
失敗Block:dispatch_semaphore_signal(semaphore);
}];
//只有信號(hào)量>1 下面代碼才走鞠呈,方法才算執(zhí)行完畢
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
以上都是我根據(jù)別人的博客和自己代碼實(shí)踐所得融师,如有冒犯和不足歡迎大家批評(píng)。
感覺(jué)在微小簡(jiǎn)單的東西蚁吝,如果系統(tǒng)學(xué)習(xí)起來(lái)旱爆,還是有盲點(diǎn)的,所以說(shuō)所有的事情態(tài)度要端正窘茁,虛心點(diǎn)吧
引用博客
http://www.reibang.com/p/2d57c72016c6
http://blog.csdn.net/Cloudox_/article/details/71107179
http://www.reibang.com/p/228403206664
https://www.cnblogs.com/zhou--fei/p/6747938.html
http://www.reibang.com/p/aa3cfcabb470