iOS多線程GCD使用方式

一运悲、基本介紹

Grand Central Dispatch (GCD)是Apple開發(fā)的一個(gè)多核編程的較新的解決方法。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對(duì)稱多處理系統(tǒng)。它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)雏逾。

GCD是一個(gè)替代諸如NSThread等技術(shù)的很高效和強(qiáng)大的技術(shù)今豆。GCD完全可以處理諸如數(shù)據(jù)鎖定和資源泄漏等復(fù)雜的異步編程問題灯蝴。GCD的工作原理是讓一個(gè)程序,根據(jù)可用的處理資源聂渊,安排他們?cè)谌魏慰捎玫奶幚砥骱诵纳掀叫信抨?duì)執(zhí)行特定的任務(wù)差购。這個(gè)任務(wù)可以是一個(gè)功能或者一個(gè)程序段。

GCD中的一個(gè)任務(wù)可被用于創(chuàng)造一個(gè)被放置于隊(duì)列的工作項(xiàng)目或者事件源汉嗽。如果一個(gè)任務(wù)被分配到一個(gè)事件源欲逃,那么一個(gè)由功能或者程序塊組成的工作單元會(huì)被放置于一個(gè)適當(dāng)?shù)年?duì)列中。蘋果公司認(rèn)為GCD相比于普通的一個(gè)接一個(gè)的執(zhí)行任務(wù)的方式更為有效率饼暑。

使用GCD 有以下好處:
  • GCD可用于多核的并行運(yùn)算稳析;
  • GCD會(huì)自動(dòng)利用更多的 CPU 內(nèi)核(比如雙核、四核)弓叛;
  • GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程彰居、調(diào)度任務(wù)、銷毀線程)撰筷;
  • 只需要告訴GCD想要執(zhí)行什么任務(wù)陈惰,不需要編寫任何線程管理代碼,因?yàn)榫€程是安全的毕籽。

二抬闯、使用方式

GCD的使用需要任務(wù)和隊(duì)列的相互配合:

任務(wù):就是執(zhí)行GCD中放在 block 中的代碼塊。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行(sync)和異步執(zhí)行(async)关筒。兩者的主要區(qū)別是:是否等待隊(duì)列的任務(wù)執(zhí)行結(jié)束溶握,以及是否具備開啟新線程的能力。

  • 同步執(zhí)行(sync):同步添加任務(wù)到指定的隊(duì)列中蒸播,該任務(wù)會(huì)在等待指定隊(duì)列中其他任務(wù)執(zhí)行完成后再執(zhí)行睡榆。而添加的任務(wù)只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力。
    同步執(zhí)行創(chuàng)建方式:
//同步執(zhí)行
dispatch_sync(queue, ^{
   //添加執(zhí)行任務(wù)
});
  • 異步執(zhí)行(async):異步添加任務(wù)到指定的隊(duì)列中肉微,該任務(wù)不會(huì)等待隊(duì)列中其他任務(wù)執(zhí)行完成匾鸥,直接開始執(zhí)行。而添加的任務(wù)可以在新的線程中執(zhí)行任務(wù)碉纳,具備開啟新線程的能力(開啟一個(gè)還是多個(gè)線程需要根據(jù)執(zhí)行隊(duì)列來確定)勿负。
    異步執(zhí)行創(chuàng)建方式:
//異步執(zhí)行
dispatch_async(queue, ^{
   //添加執(zhí)行任務(wù)
});

其中的queue就是我們所需要?jiǎng)?chuàng)建的執(zhí)行隊(duì)列。

隊(duì)列(Dispatch Queue):這里的隊(duì)列指執(zhí)行任務(wù)的等待隊(duì)列劳曹,即用來存放任務(wù)的隊(duì)列奴愉。隊(duì)列是一種特殊的線性表,采用 FIFO(先進(jìn)先出)的原則铁孵,即新任務(wù)總是被插入到隊(duì)列的末尾锭硼,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取。每讀取一個(gè)任務(wù)蜕劝,則從隊(duì)列中釋放一個(gè)任務(wù)檀头。在 GCD 中有兩種隊(duì)列:串行隊(duì)列并發(fā)隊(duì)列。兩者都符合 FIFO(先進(jìn)先出)的原則岖沛。兩者的主要區(qū)別是:執(zhí)行順序不同暑始,以及開啟線程數(shù)不同。

  • 串行隊(duì)列(Serial Dispatch Queue):只開啟一個(gè)線程婴削,在線程中任務(wù)是按順序的一個(gè)接一個(gè)的執(zhí)行廊镜。
    串行隊(duì)列創(chuàng)建方式:
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
//創(chuàng)建主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();

其中主隊(duì)列是GCD提供的一種特殊的串行隊(duì)列。

  • 并發(fā)隊(duì)列(Concurrent Dispatch Queue):根據(jù)任務(wù)執(zhí)行的方式(同步執(zhí)行或異步執(zhí)行)來確定開啟一個(gè)或者多個(gè)線程唉俗。同步執(zhí)行時(shí)是在開啟的一個(gè)線程中按順序同步執(zhí)行嗤朴。異步執(zhí)行時(shí)是在開啟的多個(gè)線程中并發(fā)(同時(shí))執(zhí)行任務(wù)。
    并行隊(duì)列創(chuàng)建方式:
//創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

其中全局并發(fā)隊(duì)列是GCD提供的一種特殊的并發(fā)隊(duì)列虫溜。

下面介紹一下GCD任務(wù)和隊(duì)列的組合使用方式:

1雹姊、同步執(zhí)行 + 并發(fā)隊(duì)列
  • 同步執(zhí)行不具備開啟新線程的能力,因此所有任務(wù)都是在當(dāng)前線程(主線程)中執(zhí)行的吼渡。
  • 雖然并發(fā)隊(duì)列可以開啟多個(gè)線程并且可以同時(shí)執(zhí)行多個(gè)任務(wù)容为,但是因?yàn)橥綀?zhí)行任務(wù)不具備開啟新線程的能力乓序,因此只有當(dāng)前線程這一個(gè)線程寺酪,也就不存在并發(fā)。而且當(dāng)前線程只有等待當(dāng)前隊(duì)列中正在執(zhí)行的任務(wù)執(zhí)行完畢之后替劈,才能繼續(xù)接著執(zhí)行下面的操作(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)寄雀。所以任務(wù)只能一個(gè)接一個(gè)按順序執(zhí)行,不能同時(shí)被執(zhí)行陨献。
//1.同步執(zhí)行 + 并發(fā)隊(duì)列
- (instancetype)initSyncAndConcurrentDispatch {
    if (self = [super init]) {
        NSLog(@"currentThread---%@",[NSThread currentThread]);//打印當(dāng)前線程
        NSLog(@"SyncAndConcurrent---begin");
        
        dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);//創(chuàng)建并發(fā)隊(duì)列
        dispatch_sync(queue, ^{//同步執(zhí)行
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"1---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        dispatch_sync(queue, ^{//同步執(zhí)行
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"2---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        dispatch_sync(queue, ^{//同步執(zhí)行
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"3---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        NSLog(@"SyncAndConcurrent---end");
    }
    return self;
}

輸出結(jié)果:

2018-07-18 14:06:25.808498+0800 GCD[3487:140302] currentThread---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:06:25.808716+0800 GCD[3487:140302] SyncAndConcurrent---begin
2018-07-18 14:06:27.810127+0800 GCD[3487:140302] 1---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:06:29.811518+0800 GCD[3487:140302] 1---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:06:31.812374+0800 GCD[3487:140302] 2---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:06:33.812720+0800 GCD[3487:140302] 2---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:06:35.813879+0800 GCD[3487:140302] 3---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:06:37.814369+0800 GCD[3487:140302] 3---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:06:37.814666+0800 GCD[3487:140302] SyncAndConcurrent---end
2盒犹、異步執(zhí)行 + 并發(fā)隊(duì)列
  • 異步執(zhí)行具備開啟新線程的能力。且并發(fā)隊(duì)列可開啟多個(gè)線程,同時(shí)執(zhí)行多個(gè)任務(wù)急膀。因此除了當(dāng)前線程(主線程)沮协,系統(tǒng)又開啟了多個(gè)線程,并且任務(wù)是同時(shí)執(zhí)行的卓嫂。
  • 因?yàn)槭遣l(fā)隊(duì)列慷暂,所以當(dāng)前線程中所有任務(wù)沒有等待,而是直接開啟了新線程晨雳,在新線程中執(zhí)行任務(wù)(異步執(zhí)行不做等待行瑞,可以繼續(xù)執(zhí)行任務(wù))。
//2.異步執(zhí)行 + 并發(fā)隊(duì)列
- (instancetype)initAsyncAndConcurrentDispatch {
    if (self = [super init]) {
        NSLog(@"currentThread---%@",[NSThread currentThread]);//打印當(dāng)前線程
        NSLog(@"AsyncAndConcurrent---begin");
        
        dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);//創(chuàng)建并發(fā)隊(duì)列
        dispatch_async(queue, ^{//異步執(zhí)行
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"1---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        dispatch_async(queue, ^{//異步執(zhí)行
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"2---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        dispatch_async(queue, ^{//異步執(zhí)行
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"3---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        NSLog(@"AsyncAndConcurrent---end");
    }
    return self;
}

輸出結(jié)果:

2018-07-18 14:13:36.852829+0800 GCD[3487:140302] currentThread---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:13:36.853047+0800 GCD[3487:140302] AsyncAndConcurrent---begin
2018-07-18 14:13:36.853189+0800 GCD[3487:140302] AsyncAndConcurrent---end
2018-07-18 14:13:38.854025+0800 GCD[3487:140559] 2---<NSThread: 0x60400027cc40>{number = 4, name = (null)}
2018-07-18 14:13:38.854027+0800 GCD[3487:145869] 1---<NSThread: 0x600000462780>{number = 3, name = (null)}
2018-07-18 14:13:38.854025+0800 GCD[3487:145877] 3---<NSThread: 0x604000277c00>{number = 5, name = (null)}
2018-07-18 14:13:40.858188+0800 GCD[3487:145869] 1---<NSThread: 0x600000462780>{number = 3, name = (null)}
2018-07-18 14:13:40.858188+0800 GCD[3487:140559] 2---<NSThread: 0x60400027cc40>{number = 4, name = (null)}
2018-07-18 14:13:40.858288+0800 GCD[3487:145877] 3---<NSThread: 0x604000277c00>{number = 5, name = (null)}
3餐禁、同步執(zhí)行 + 串行隊(duì)列
  • 同步執(zhí)行不具備開啟新線程的能力血久,因此所有任務(wù)都是在當(dāng)前線程(主線程)中執(zhí)行的。
  • 因?yàn)槭谴嘘?duì)列帮非,所以任務(wù)是一個(gè)接一個(gè)按順序執(zhí)行氧吐,并且每次只有一個(gè)任務(wù)被執(zhí)行。
//3.同步執(zhí)行 + 串行隊(duì)列
- (instancetype)initSyncAndSerialDispatch {
    if (self = [super init]) {
        NSLog(@"currentThread---%@",[NSThread currentThread]);//打印當(dāng)前線程
        NSLog(@"SyncAndSerial---begin");
        
        dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);//創(chuàng)建串行隊(duì)列
        dispatch_sync(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];           //模擬耗時(shí)操作
                NSLog(@"1---%@",[NSThread currentThread]);   //打印當(dāng)前線程
            }
        });
        dispatch_sync(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];           //模擬耗時(shí)操作
                NSLog(@"2---%@",[NSThread currentThread]);   //打印當(dāng)前線程
            }
        });
        dispatch_sync(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];           //模擬耗時(shí)操作
                NSLog(@"3---%@",[NSThread currentThread]);   //打印當(dāng)前線程
            }
        });
        NSLog(@"SyncAndSerial---end");
    }
    return self;
}

輸出結(jié)果:

2018-07-18 14:18:52.586924+0800 GCD[3487:140302] currentThread---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:18:52.587135+0800 GCD[3487:140302] SyncAndSerial---begin
2018-07-18 14:18:54.587815+0800 GCD[3487:140302] 1---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:18:56.588782+0800 GCD[3487:140302] 1---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:18:58.590248+0800 GCD[3487:140302] 2---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:19:00.591183+0800 GCD[3487:140302] 2---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:19:02.591603+0800 GCD[3487:140302] 3---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:19:04.593199+0800 GCD[3487:140302] 3---<NSThread: 0x604000065440>{number = 1, name = main}
2018-07-18 14:19:04.593466+0800 GCD[3487:140302] SyncAndSerial---end
4末盔、異步執(zhí)行 + 串行隊(duì)列
  • 異步執(zhí)行具備開啟新線程的能力副砍。但由于是串行隊(duì)列,只能開啟一個(gè)線程庄岖。因此除了當(dāng)前線程(主線程)豁翎,系統(tǒng)又開啟了一個(gè)線程。
  • 因?yàn)槭谴嘘?duì)列隅忿,所以任務(wù)是一個(gè)接一個(gè)按順序執(zhí)行心剥,并且每次只有一個(gè)任務(wù)被執(zhí)行。但串行隊(duì)列中的所有任務(wù)都是在新開啟的線程中執(zhí)行的背桐。
//4.異步執(zhí)行 + 串行隊(duì)列
- (instancetype)initAsyncAndSerialDispatch {
    if (self = [super init]) {
        NSLog(@"currentThread---%@",[NSThread currentThread]);//打印當(dāng)前線程
        NSLog(@"AsyncAndSerial---begin");
        
        dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);//創(chuàng)建串行隊(duì)列
        dispatch_async(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"1---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        dispatch_async(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"2---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        dispatch_async(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"3---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        NSLog(@"AsyncAndSerial---end");
    }
    return self;
}

輸出結(jié)果:

2018-07-18 14:25:13.368635+0800 GCD[3787:154025] currentThread---<NSThread: 0x604000066400>{number = 1, name = main}
2018-07-18 14:25:13.368890+0800 GCD[3787:154025] AsyncAndSerial---begin
2018-07-18 14:25:13.369052+0800 GCD[3787:154025] AsyncAndSerial---end
2018-07-18 14:25:15.373476+0800 GCD[3787:154089] 1---<NSThread: 0x6000002709c0>{number = 3, name = (null)}
2018-07-18 14:25:17.377079+0800 GCD[3787:154089] 1---<NSThread: 0x6000002709c0>{number = 3, name = (null)}
2018-07-18 14:25:19.381357+0800 GCD[3787:154089] 2---<NSThread: 0x6000002709c0>{number = 3, name = (null)}
2018-07-18 14:25:21.382271+0800 GCD[3787:154089] 2---<NSThread: 0x6000002709c0>{number = 3, name = (null)}
2018-07-18 14:25:23.384662+0800 GCD[3787:154089] 3---<NSThread: 0x6000002709c0>{number = 3, name = (null)}
2018-07-18 14:25:25.386662+0800 GCD[3787:154089] 3---<NSThread: 0x6000002709c0>{number = 3, name = (null)}
5优烧、同步執(zhí)行 + 主隊(duì)列(會(huì)造成死鎖)
  • 在主線程中使用同步執(zhí)行 + 主隊(duì)列會(huì)造成死鎖
//5.同步執(zhí)行 + 主隊(duì)列(會(huì)造成死鎖)
- (instancetype)initSyncAndMainDispatch {
    if (self = [super init]) {
        NSLog(@"currentThread---%@",[NSThread currentThread]);//打印當(dāng)前線程
        NSLog(@"SyncAndMain---begin");
        
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_sync(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
                NSLog(@"1---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        NSLog(@"SyncAndMain---end");
    }
    return self;
}

輸出結(jié)果:

2018-07-18 14:31:31.002501+0800 GCD[3787:154025] currentThread---<NSThread: 0x604000066400>{number = 1, name = main}
2018-07-18 14:31:31.002743+0800 GCD[3787:154025] SyncAndMain---begin

由此可以看出:

  • 在主線程中使用同步執(zhí)行 + 主隊(duì)列,追加到主線程的任務(wù)1不再執(zhí)行了链峭,而且syncMain---end也沒有打印
  • 死鎖原因:這是因?yàn)槲覀冊(cè)谥骶€程中添加同步操作時(shí)畦娄,同步操作要等主線程的任務(wù)執(zhí)行完成才開始執(zhí)行,此時(shí)同步操作處于等待狀態(tài)弊仪。而主線程任務(wù)中含有已經(jīng)添加的同步操作熙卡,而主線程只有等同步操作執(zhí)行完成才能繼續(xù)操作,而此時(shí)的同步操作處于等待狀態(tài)励饵,所以主線程會(huì)等待同步操作執(zhí)行完成才繼續(xù)執(zhí)行驳癌,從而兩者相互等待,以至于會(huì)造成線程死鎖役听。
解決死鎖的辦法:把需要在主隊(duì)列中同步執(zhí)行的任務(wù)放到其他線程中去颓鲜。
//同步執(zhí)行 + 主隊(duì)列(解決死鎖調(diào)用方法)
- (instancetype)initSyncAndMainDispatchResolve {
    if (self = [super init]) {
        NSLog(@"currentThread---%@",[NSThread currentThread]);//打印當(dāng)前線程
        NSLog(@"SyncAndMain---begin");
        //創(chuàng)建其他線程中調(diào)用同步執(zhí)行 + 主隊(duì)列
        [NSThread detachNewThreadSelector:@selector(SyncMain) toTarget:self withObject:nil];
    }
    return self;
}

- (void)SyncMain {
    NSLog(@"currentThread 4---%@",[NSThread currentThread]);//打印當(dāng)前線程
    NSLog(@"SyncAndMain 4---begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        for (int i=0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
            NSLog(@"1---%@",[NSThread currentThread]);    //打印當(dāng)前線程
        }
    });
    dispatch_sync(queue, ^{
        for (int i=0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
            NSLog(@"2---%@",[NSThread currentThread]);    //打印當(dāng)前線程
        }
    });
    dispatch_sync(queue, ^{
        for (int i=0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)操作
            NSLog(@"2---%@",[NSThread currentThread]);    //打印當(dāng)前線程
        }
    });
    NSLog(@"SyncAndMain---end");
}

輸出結(jié)果:

2018-07-18 14:38:07.108703+0800 GCD[4000:163392] currentThread---<NSThread: 0x60400006b600>{number = 1, name = main}
2018-07-18 14:38:07.108999+0800 GCD[4000:163392] SyncAndMain---begin
2018-07-18 14:38:07.110803+0800 GCD[4000:164570] currentThread 4---<NSThread: 0x600000468240>{number = 3, name = (null)}
2018-07-18 14:38:07.111920+0800 GCD[4000:164570] SyncAndMain 4---begin
2018-07-18 14:38:09.114080+0800 GCD[4000:163392] 1---<NSThread: 0x60400006b600>{number = 1, name = main}
2018-07-18 14:38:11.115053+0800 GCD[4000:163392] 1---<NSThread: 0x60400006b600>{number = 1, name = main}
2018-07-18 14:38:13.116707+0800 GCD[4000:163392] 2---<NSThread: 0x60400006b600>{number = 1, name = main}
2018-07-18 14:38:15.117198+0800 GCD[4000:163392] 2---<NSThread: 0x60400006b600>{number = 1, name = main}
2018-07-18 14:38:17.118623+0800 GCD[4000:163392] 2---<NSThread: 0x60400006b600>{number = 1, name = main}
2018-07-18 14:38:19.120149+0800 GCD[4000:163392] 2---<NSThread: 0x60400006b600>{number = 1, name = main}
2018-07-18 14:38:19.120473+0800 GCD[4000:164570] SyncAndMain---end
6表窘、異步執(zhí)行 + 主隊(duì)列
  • 雖然異步執(zhí)行具備開啟線程的能力,但因?yàn)槭侵麝?duì)列甜滨,所以所有任務(wù)都是在當(dāng)前線程(主線程)中執(zhí)行的乐严,并沒有開啟新的線程。
  • 因?yàn)槭谴嘘?duì)列衣摩,所以任務(wù)是一個(gè)接一個(gè)按順序執(zhí)行麦备,并且每次只有一個(gè)任務(wù)被執(zhí)行。
//6.異步執(zhí)行 + 主隊(duì)列
- (instancetype)initAsyncAndMainDispatch {
    if (self = [super init]) {
        NSLog(@"currentThread---%@",[NSThread currentThread]);//打印當(dāng)前線程
        NSLog(@"AsyncAndMain---begin");
        
        dispatch_queue_t queue = dispatch_get_main_queue();   //獲取主線程
        dispatch_async(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)線程
                NSLog(@"1---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        dispatch_async(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)線程
                NSLog(@"2---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        dispatch_async(queue, ^{
            for (int i=0; i<2; i++) {
                [NSThread sleepForTimeInterval:2];            //模擬耗時(shí)線程
                NSLog(@"3---%@",[NSThread currentThread]);    //打印當(dāng)前線程
            }
        });
        NSLog(@"AsyncAndMain---end");
    }
    return self;
}

輸出結(jié)果:

2018-07-18 14:44:03.921354+0800 GCD[4125:168926] currentThread---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 14:44:03.921575+0800 GCD[4125:168926] AsyncAndMain---begin
2018-07-18 14:44:03.921763+0800 GCD[4125:168926] AsyncAndMain---end
2018-07-18 14:44:05.926029+0800 GCD[4125:168926] 1---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 14:44:07.928790+0800 GCD[4125:168926] 1---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 14:44:09.931443+0800 GCD[4125:168926] 2---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 14:44:11.933365+0800 GCD[4125:168926] 2---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 14:44:13.935818+0800 GCD[4125:168926] 3---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 14:44:15.937064+0800 GCD[4125:168926] 3---<NSThread: 0x60000006ce00>{number = 1, name = main}
7昭娩、同步執(zhí)行 + 全局并發(fā)隊(duì)列

由于全局并發(fā)隊(duì)列等同于普通并發(fā)隊(duì)列凛篙,所以該操作等同于同步執(zhí)行 + 并發(fā)隊(duì)列,請(qǐng)參考 1栏渺、同步執(zhí)行 + 并發(fā)隊(duì)列呛梆。

8、異步執(zhí)行 + 全局并發(fā)隊(duì)列

由于全局并發(fā)隊(duì)列等同于普通并發(fā)隊(duì)列磕诊,所以該操作等同于異步執(zhí)行 + 并發(fā)隊(duì)列填物,請(qǐng)參考 2、異步執(zhí)行 + 并發(fā)隊(duì)列霎终。

9滞磺、GCD柵欄方法:dispatch_barrier_async
  • dispatch_barrier_async函數(shù)的作用是在進(jìn)程管理中起到一個(gè)柵欄的作用,它等待所有位于barrier函數(shù)之前的操作執(zhí)行完畢后執(zhí)行,并且在barrier函數(shù)執(zhí)行之后,barrier函數(shù)之后的操作才會(huì)得到執(zhí)行。
  • 該函數(shù)需要配合并發(fā)隊(duì)列(dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);)使用莱褒。
//9.GCD柵欄方法
- (void)dispatchBarrier {
    NSLog(@"currentThread --- %@",[NSThread currentThread]);
    NSLog(@"dispatchBarrier --- Begin");
    
    dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (int i=0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1---%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i=0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2---%@",[NSThread currentThread]);
        }
    });
    //柵欄方法
    dispatch_barrier_async(queue, ^{
        for (int i=0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3---%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (int i=0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"4---%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i=0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"5---%@",[NSThread currentThread]);
        }
    });
    NSLog(@"dispatchBarrier --- end");
}

輸出結(jié)果:

2018-07-18 14:59:21.577599+0800 GCD[4125:168926] currentThread --- <NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 14:59:21.577839+0800 GCD[4125:168926] dispatchBarrier --- Begin
2018-07-18 14:59:21.578039+0800 GCD[4125:168926] dispatchBarrier --- end
2018-07-18 14:59:23.581183+0800 GCD[4125:169172] 2---<NSThread: 0x604000273ec0>{number = 4, name = (null)}
2018-07-18 14:59:23.581191+0800 GCD[4125:180793] 1---<NSThread: 0x600000477880>{number = 3, name = (null)}
2018-07-18 14:59:25.585534+0800 GCD[4125:169172] 2---<NSThread: 0x604000273ec0>{number = 4, name = (null)}
2018-07-18 14:59:25.585605+0800 GCD[4125:180793] 1---<NSThread: 0x600000477880>{number = 3, name = (null)}
2018-07-18 14:59:27.589468+0800 GCD[4125:180793] 3---<NSThread: 0x600000477880>{number = 3, name = (null)}
2018-07-18 14:59:29.594449+0800 GCD[4125:180793] 3---<NSThread: 0x600000477880>{number = 3, name = (null)}
2018-07-18 14:59:31.599255+0800 GCD[4125:180793] 4---<NSThread: 0x600000477880>{number = 3, name = (null)}
2018-07-18 14:59:31.599255+0800 GCD[4125:169172] 5---<NSThread: 0x604000273ec0>{number = 4, name = (null)}
2018-07-18 14:59:33.603883+0800 GCD[4125:180793] 4---<NSThread: 0x600000477880>{number = 3, name = (null)}
2018-07-18 14:59:33.603883+0800 GCD[4125:169172] 5---<NSThread: 0x604000273ec0>{number = 4, name = (null)}
10击困、GCD延時(shí)執(zhí)行方法:dispatch_after
  • dispatch_after函數(shù)可以實(shí)現(xiàn)在指定時(shí)間之后開始執(zhí)行某個(gè)任務(wù)。需要注意的是dispatch_after函數(shù)并不是在指定時(shí)間之后才開始執(zhí)行處理广凸,而是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中阅茶。嚴(yán)格來說,這個(gè)時(shí)間并不是絕對(duì)準(zhǔn)確的谅海,但想要大致延遲執(zhí)行任務(wù)脸哀,dispatch_after函數(shù)是很有效的。
//10.GCD延時(shí)執(zhí)行方法
- (void)dispatchAfter {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  //打印當(dāng)前線程
    NSLog(@"dispatchAfter---begin");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"after---%@",[NSThread currentThread]);      //打印當(dāng)前線程
    });
    NSLog(@"dispatchAfter---end");
}

輸出結(jié)果:

2018-07-18 15:08:27.072470+0800 GCD[4125:168926] currentThread---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 15:08:27.072737+0800 GCD[4125:168926] dispatchAfter---begin
2018-07-18 15:08:27.072927+0800 GCD[4125:168926] dispatchAfter---end
2018-07-18 15:08:29.073087+0800 GCD[4125:168926] after---<NSThread: 0x60000006ce00>{number = 1, name = main}
11扭吁、GCD單例:dispatch_once
  • 使用dispatch_once 函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行一次撞蜂,并且即使在多線程的環(huán)境下,dispatch_once也可以保證線程安全侥袜。
#import "DispatchOnce.h"

static DispatchOnce * _onceManager;
@implementation DispatchOnce

//單例
+ (instancetype)dispatchOnceManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _onceManager = [[DispatchOnce alloc]init];
    });
    return _onceManager;
}

+ (instancetype)dispatchManager {
    _onceManager = [[DispatchOnce alloc]init];
    return _onceManager;
}

@end
//打印創(chuàng)建對(duì)象地址
DispatchOnce * one = [DispatchOnce dispatchOnceManager];
DispatchOnce * two = [DispatchOnce dispatchOnceManager];
DispatchOnce * three = [DispatchOnce dispatchManager];
DispatchOnce * four = [DispatchOnce dispatchManager];
NSLog(@"one  :%@",one);
NSLog(@"two  :%@",two);
NSLog(@"three:%@",three);
NSLog(@"four :%@",four);

輸出結(jié)果:

2018-07-18 15:12:01.385630+0800 GCD[4125:168926] one  :<DispatchOnce: 0x60400001bfd0>
2018-07-18 15:12:01.385830+0800 GCD[4125:168926] two  :<DispatchOnce: 0x60400001bfd0>
2018-07-18 15:12:01.385957+0800 GCD[4125:168926] three:<DispatchOnce: 0x60400001b6a0>
2018-07-18 15:12:01.386151+0800 GCD[4125:168926] four :<DispatchOnce: 0x60400001aad0>
12蝌诡、GCD快速迭代方法:dispatch_apply
  • dispatch_apply按照指定的次數(shù)將指定的任務(wù)追加到指定的隊(duì)列中,并等待全部隊(duì)列執(zhí)行結(jié)束系馆。
  • 因?yàn)槭窃诓l(fā)隊(duì)列中異步執(zhí)行任務(wù)送漠,所以各個(gè)任務(wù)的執(zhí)行時(shí)間長短不定,最后結(jié)束順序也不定由蘑。但是線程會(huì)等待dispatch_apply函數(shù)中全部任務(wù)執(zhí)行完畢才會(huì)繼續(xù)執(zhí)行其他任務(wù)闽寡。
//12.GCD 快速迭代方法:dispatch_apply
- (instancetype)initDispatchApplyManager {
    if (self = [super init]) {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        NSLog(@"apply---begin");
        dispatch_apply(5, queue, ^(size_t index) {
            NSLog(@"%ld --- %@",index,[NSThread currentThread]);
        });
        NSLog(@"apply---end");
    }
    return self;
}

輸出結(jié)果:

2018-07-18 15:18:23.522423+0800 GCD[4125:168926] apply---begin
2018-07-18 15:18:23.524855+0800 GCD[4125:168926] 0 --- <NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 15:18:23.524875+0800 GCD[4125:194598] 1 --- <NSThread: 0x604000274b00>{number = 5, name = (null)}
2018-07-18 15:18:23.524926+0800 GCD[4125:180800] 2 --- <NSThread: 0x600000478000>{number = 6, name = (null)}
2018-07-18 15:18:23.524982+0800 GCD[4125:194608] 3 --- <NSThread: 0x604000271b80>{number = 7, name = (null)}
2018-07-18 15:18:23.525190+0800 GCD[4125:168926] 4 --- <NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 15:18:23.525362+0800 GCD[4125:168926] apply---end
13、GCD隊(duì)列組:dispatch_group

如果我們?cè)谝粋€(gè)線程中異步執(zhí)行幾個(gè)耗時(shí)操作尼酿,但我們想等這幾個(gè)耗時(shí)操作都完成之后再去執(zhí)行其他操作爷狈,這時(shí)候我們就可以用到隊(duì)列組dispatch_group了。

使用方式:調(diào)用隊(duì)列組的 dispatch_group_async 先把任務(wù)放到隊(duì)列中裳擎,然后將隊(duì)列放入隊(duì)列組中涎永。或者使用隊(duì)列組的dispatch_group_enter鹿响、dispatch_group_leave 組合來實(shí)現(xiàn)dispatch_group_async羡微。或者調(diào)用隊(duì)列組的 dispatch_group_notify 回到指定線程執(zhí)行任務(wù)惶我÷杈螅或者使用dispatch_group_wait 回到當(dāng)前線程繼續(xù)向下執(zhí)行(會(huì)阻塞當(dāng)前線程)

dispatch_group_notify
- (IBAction)dispatch_group_notify:(id)sender {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程
    NSLog(@"group---begin");
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    dispatch_group_async(group, globalQueue, ^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
    });
    dispatch_group_async(group, globalQueue, ^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
    });
    dispatch_group_notify(group, mainQueue, ^{
        // 等前面的異步任務(wù)1、任務(wù)2都執(zhí)行完畢后绸贡,回到主線程執(zhí)行下邊任務(wù)
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
        NSLog(@"group---end");
    });
}

輸出結(jié)果:

2018-07-18 15:40:04.310925+0800 GCD[4125:168926] currentThread---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 15:40:04.311191+0800 GCD[4125:168926] group---begin
2018-07-18 15:40:06.316638+0800 GCD[4125:208985] 1---<NSThread: 0x6000004666c0>{number = 8, name = (null)}
2018-07-18 15:40:06.316645+0800 GCD[4125:180800] 2---<NSThread: 0x600000478000>{number = 6, name = (null)}
2018-07-18 15:40:08.319994+0800 GCD[4125:180800] 2---<NSThread: 0x600000478000>{number = 6, name = (null)}
2018-07-18 15:40:08.320003+0800 GCD[4125:208985] 1---<NSThread: 0x6000004666c0>{number = 8, name = (null)}
2018-07-18 15:40:10.321659+0800 GCD[4125:168926] 3---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 15:40:12.322667+0800 GCD[4125:168926] 3---<NSThread: 0x60000006ce00>{number = 1, name = main}
2018-07-18 15:40:12.323029+0800 GCD[4125:168926] group---end
  • 監(jiān)聽 group 中任務(wù)的完成狀態(tài)盯蝴,當(dāng)所有的任務(wù)都執(zhí)行完成后,追加任務(wù)到 group 中听怕,并執(zhí)行任務(wù)捧挺。
  • 從dispatch_group_notify相關(guān)代碼運(yùn)行輸出結(jié)果可以看出:
    當(dāng)所有任務(wù)都執(zhí)行完成之后,才執(zhí)行dispatch_group_notify block 中的任務(wù)尿瞭。
dispatch_group_wait
- (IBAction)dispatch_group_wait:(id)sender {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_async(group, globalQueue, ^{
        // 追加任務(wù)1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
    });

    dispatch_group_async(group, globalQueue, ^{
        // 追加任務(wù)2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
    });
    //等待上面的任務(wù)全部完成后闽烙,會(huì)往下繼續(xù)執(zhí)行(會(huì)阻塞當(dāng)前線程)
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"group---end");
}

輸出結(jié)果:

2018-07-18 15:46:21.438719+0800 GCD[5117:214014] currentThread---<NSThread: 0x600000260d80>{number = 1, name = main}
2018-07-18 15:46:21.439135+0800 GCD[5117:214014] group---begin
2018-07-18 15:46:23.440102+0800 GCD[5117:214604] 1---<NSThread: 0x60400046d440>{number = 4, name = (null)}
2018-07-18 15:46:23.440101+0800 GCD[5117:214057] 2---<NSThread: 0x600000666f00>{number = 3, name = (null)}
2018-07-18 15:46:25.446875+0800 GCD[5117:214057] 2---<NSThread: 0x600000666f00>{number = 3, name = (null)}
2018-07-18 15:46:25.447304+0800 GCD[5117:214604] 1---<NSThread: 0x60400046d440>{number = 4, name = (null)}
2018-07-18 15:46:25.453273+0800 GCD[5117:214014] group---end
  • dispatch_group_wait會(huì)暫停當(dāng)前線程(阻塞當(dāng)前線程),等待指定的 group 中的任務(wù)執(zhí)行完成后声搁,才會(huì)往下繼續(xù)執(zhí)行鸣峭。
dispatch_group_enter、dispatch_group_leave
- (IBAction)dispatch_groupClick:(id)sender {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程
    NSLog(@"group---begin");
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 追加任務(wù)1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 追加任務(wù)2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的異步操作都執(zhí)行完畢后酥艳,回到主線程.
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
        NSLog(@"group---end");
    });
    
    //    // 等待上面的任務(wù)全部完成后摊溶,會(huì)往下繼續(xù)執(zhí)行(會(huì)阻塞當(dāng)前線程)
    //    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    //    NSLog(@"group---end");
}

輸出結(jié)果:

2018-07-18 15:52:12.940112+0800 GCD[5242:219381] currentThread---<NSThread: 0x600000069200>{number = 1, name = main}
2018-07-18 15:52:12.940382+0800 GCD[5242:219381] group---begin
2018-07-18 15:52:14.946018+0800 GCD[5242:219651] 2---<NSThread: 0x600000469500>{number = 3, name = (null)}
2018-07-18 15:52:14.946018+0800 GCD[5242:219432] 1---<NSThread: 0x6000004762c0>{number = 4, name = (null)}
2018-07-18 15:52:16.951382+0800 GCD[5242:219432] 1---<NSThread: 0x6000004762c0>{number = 4, name = (null)}
2018-07-18 15:52:16.951397+0800 GCD[5242:219651] 2---<NSThread: 0x600000469500>{number = 3, name = (null)}
2018-07-18 15:52:18.953450+0800 GCD[5242:219381] 3---<NSThread: 0x600000069200>{number = 1, name = main}
2018-07-18 15:52:20.954745+0800 GCD[5242:219381] 3---<NSThread: 0x600000069200>{number = 1, name = main}
2018-07-18 15:52:20.954954+0800 GCD[5242:219381] group---end
  • dispatch_group_enter 標(biāo)志著一個(gè)任務(wù)追加到 group,執(zhí)行一次充石,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)+1莫换。
  • dispatch_group_leave 標(biāo)志著一個(gè)任務(wù)離開了 group,執(zhí)行一次骤铃,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)-1拉岁。
  • 當(dāng) group 中未執(zhí)行完畢任務(wù)數(shù)為0的時(shí)候,才會(huì)使dispatch_group_wait解除阻塞惰爬,以及執(zhí)行追加到dispatch_group_notify中的任務(wù)喊暖。
  • 從dispatch_group_enter、dispatch_group_leave相關(guān)代碼運(yùn)行結(jié)果中可以看出:當(dāng)所有任務(wù)執(zhí)行完成之后撕瞧,才執(zhí)行 dispatch_group_notify 中的任務(wù)陵叽。這里的dispatch_group_enter狞尔、dispatch_group_leave組合,其實(shí)等同于dispatch_group_async巩掺。
14偏序、GCD信號(hào)量:dispatch_semaphore
  • 信號(hào)量就是一種可用來控制訪問資源的數(shù)量的標(biāo)識(shí),設(shè)定了一個(gè)信號(hào)量胖替,在線程訪問之前研儒,加上信號(hào)量的處理,則可告知系統(tǒng)按照我們指定的信號(hào)量數(shù)量來執(zhí)行多個(gè)線程独令。
  • 類似鎖機(jī)制端朵,只不過信號(hào)量都是系統(tǒng)幫助我們處理了,我們只需要在執(zhí)行線程之前燃箭,設(shè)定一個(gè)信號(hào)量值冲呢,并且在使用時(shí),加上信號(hào)量處理方法就行了遍膜。
Dispatch Semaphore 提供了三個(gè)函數(shù):
  • dispatch_semaphore_create:創(chuàng)建一個(gè)Semaphore并初始化信號(hào)的總量
  • dispatch_semaphore_signal:發(fā)送一個(gè)信號(hào)碗硬,讓信號(hào)總量加1
  • dispatch_semaphore_wait:可以使總信號(hào)量減1,當(dāng)信號(hào)總量為0時(shí)就會(huì)一直等待(阻塞所在線程)瓢颅,否則就可以正常執(zhí)行恩尾。
- (IBAction)useClick:(id)sender {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程
    NSLog(@"semaphore---begin");
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    __block NSInteger num = 0;
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        num = 1;
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore---end,num = %ld",num);
}

輸出結(jié)果:

2018-07-18 16:05:37.412573+0800 GCD[5472:229493] currentThread---<NSThread: 0x60400007cdc0>{number = 1, name = main}
2018-07-18 16:05:37.412940+0800 GCD[5472:229493] semaphore---begin
2018-07-18 16:05:39.417126+0800 GCD[5472:229532] 1---<NSThread: 0x60000007e080>{number = 3, name = (null)}
2018-07-18 16:05:39.417295+0800 GCD[5472:229493] semaphore---end,num = 1
  • 異步執(zhí)行不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù)挽懦。異步執(zhí)行將任務(wù)1追加到隊(duì)列之后翰意,不做等待,接著執(zhí)行dispatch_semaphore_wait方法信柿。此時(shí) semaphore == 0冀偶,當(dāng)前線程進(jìn)入等待狀態(tài)。然后渔嚷,異步任務(wù)1開始執(zhí)行进鸠。任務(wù)1執(zhí)行到dispatch_semaphore_signal之后,此時(shí)總信號(hào)量 semaphore == 1形病,dispatch_semaphore_wait方法使總信號(hào)量減1客年,正在被阻塞的線程(主線程)恢復(fù)繼續(xù)執(zhí)行。最后打印semaphore---end,number = 1漠吻。這樣就實(shí)現(xiàn)了線程同步量瓜,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。
  • Dispatch Semaphore 在實(shí)際開發(fā)中主要用于:保持線程同步途乃,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)绍傲;保證線程安全,為線程加鎖耍共。
15烫饼、取消GCD任務(wù)兩種方法:
  • 第一種:iOS8之后可以調(diào)用dispatch_block_cancel來取消(需要注意必須用dispatch_block_create創(chuàng)建dispatch_block_t)猎塞,dispatch_block_cancel也只能取消尚未執(zhí)行的任務(wù),對(duì)正在執(zhí)行的任務(wù)不起作用枫弟。
  • 第二種:定義外部變量邢享,用于標(biāo)記block是否需要取消鹏往,該方法是模擬NSOperation淡诗,在執(zhí)行block前先檢查isCancelled = YES ?在block中及時(shí)的檢測(cè)標(biāo)記變量伊履,當(dāng)發(fā)現(xiàn)需要取消時(shí)韩容,終止后續(xù)操作(如直接返回return)。

Demo地址:GCD

「歡迎指正交流」0_0

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末唐瀑,一起剝皮案震驚了整個(gè)濱河市群凶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哄辣,老刑警劉巖请梢,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異力穗,居然都是意外死亡毅弧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門当窗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來够坐,“玉大人,你說我怎么就攤上這事崖面≡” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵巫员,是天一觀的道長庶香。 經(jīng)常有香客問我,道長简识,這世上最難降的妖魔是什么赶掖? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮财异,結(jié)果婚禮上倘零,老公的妹妹穿的比我還像新娘。我一直安慰自己戳寸,他們只是感情好呈驶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疫鹊,像睡著了一般袖瞻。 火紅的嫁衣襯著肌膚如雪司致。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天聋迎,我揣著相機(jī)與錄音欠母,去河邊找鬼。 笑死胚股,一個(gè)胖子當(dāng)著我的面吹牛雷客,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牺堰,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拄轻,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了伟葫?” 一聲冷哼從身側(cè)響起恨搓,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筏养,沒想到半個(gè)月后斧抱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渐溶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年辉浦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掌猛。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盏浙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出荔茬,到底是詐尸還是另有隱情废膘,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布慕蔚,位于F島的核電站丐黄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏孔飒。R本人自食惡果不足惜灌闺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坏瞄。 院中可真熱鬧桂对,春花似錦、人聲如沸鸠匀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宅此,卻和暖如春机错,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背父腕。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國打工弱匪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人璧亮。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓萧诫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杜顺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子财搁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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

  • 本文首發(fā)于我的個(gè)人博客:「程序員充電站」[https://itcharge.cn]文章鏈接:「傳送門」[https...
    ITCharge閱讀 347,830評(píng)論 308 1,926
  • 文用來介紹 iOS 多線程中 GCD 的相關(guān)知識(shí)以及使用方法蘸炸。通過本文躬络,您將了解到: 1. GCD 簡介 2. G...
    曉_我想去環(huán)游世界閱讀 1,142評(píng)論 2 8
  • “二喜、有慶不要偷懶搭儒,家珍穷当、鳳霞耕得好,苦根也行啊淹禾∧俨耍”我說:“這牛究竟有多少名字?”老人回答:“這牛叫福貴铃岔,就一個(gè)...
    巖井俊一閱讀 557評(píng)論 0 3
  • 失戀是親密關(guān)系受損的一種汪疮,個(gè)體與父母分離時(shí)會(huì)進(jìn)入兩個(gè)階段“抗議和失望”。而這同樣能從失戀后的個(gè)體身上觀察到毁习。失戀是...
    夏靈岳閱讀 248評(píng)論 0 0
  • 怎樣才能幸福智嚷? 前兩天一直在淘寶上購物,沉浸在買買買的樂趣里纺且。 該買的都買了盏道,想買的暫時(shí)沒心情買。于是又沉寂了下來...
    桃子蕓蕓閱讀 202評(píng)論 0 1