GCD 系列知識(shí)總結(jié)

image.png

引言: 越是細(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ì)造成死鎖。


image.png
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í)行順序:


image.png

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_waitdispatch_group_notify 同時(shí)出現(xiàn),只有dispatch_group_wait執(zhí)行后水孩, dispatch_group_notify才會(huì)執(zhí)行镰矿,不管dispatch_group_waitdispatch_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_enterdispatch_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_enterdispatch_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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末怀伦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子山林,更是在濱河造成了極大的恐慌房待,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驼抹,死亡現(xiàn)場(chǎng)離奇詭異桑孩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)框冀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)流椒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人明也,你說(shuō)我怎么就攤上這事宣虾。” “怎么了温数?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵绣硝,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我帆吻,道長(zhǎng),這世上最難降的妖魔是什么咙边? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任猜煮,我火速辦了婚禮,結(jié)果婚禮上败许,老公的妹妹穿的比我還像新娘王带。我一直安慰自己,他們只是感情好市殷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布愕撰。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搞挣。 梳的紋絲不亂的頭發(fā)上带迟,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音囱桨,去河邊找鬼仓犬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舍肠,可吹牛的內(nèi)容都是我干的搀继。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼翠语,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼叽躯!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起肌括,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤点骑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后们童,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體畔况,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年慧库,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跷跪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡齐板,死狀恐怖吵瞻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情甘磨,我是刑警寧澤橡羞,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站济舆,受9級(jí)特大地震影響卿泽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滋觉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一签夭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧椎侠,春花似錦第租、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丐吓。三九已至,卻和暖如春趟据,著一層夾襖步出監(jiān)牢的瞬間券犁,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工之宿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留族操,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓比被,卻偏偏與公主長(zhǎng)得像色难,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子等缀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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

  • 本文首發(fā)于我的個(gè)人博客:「程序員充電站」[https://itcharge.cn]文章鏈接:「?jìng)魉烷T(mén)」[https...
    ITCharge閱讀 347,548評(píng)論 308 1,926
  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了枷莉!去的時(shí)候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似尺迂。...
    Dely閱讀 9,229評(píng)論 21 42
  • 我的性格需要書(shū)寫(xiě)笤妙,因?yàn)闅g樂(lè)生活中的我尤其沉默,秘密使我如有重負(fù)噪裕,步履艱難蹲盘,大山中身背柴禾的人常常如此。我被秘密壓迫...
    楊知行閱讀 617評(píng)論 0 0
  • 1. 當(dāng)一個(gè)人總用一種思維方式去解決問(wèn)題時(shí),他會(huì)很容易陷入困境祭陷。 其實(shí)事實(shí)的起源是一道數(shù)學(xué)題苍凛,今天晚上,我姨媽發(fā)微...
    勞倫斯公園閱讀 904評(píng)論 11 14
  • (叁)回鄉(xiāng) 第二天起來(lái)后兵志,我繼續(xù)去咖啡館打工醇蝴。小年夜過(guò)后,路上已經(jīng)沒(méi)有什么人想罕,也不堵車了悠栓,看來(lái),北京還是一個(gè)以外來(lái)...
    顧一念閱讀 401評(píng)論 9 4