GCD

問(wèn)題
  • 主要是runloop的原理以及核心源代碼

  • gcd的原理及應(yīng)用,source的原理為什么比timer更精準(zhǔn);

定義

GCD是異步執(zhí)行任務(wù)的技術(shù)之一;一般將應(yīng)用程序中記述的線(xiàn)程管理利用的代碼在系統(tǒng)級(jí)中實(shí)現(xiàn)。
開(kāi)發(fā)者只需要定義想要執(zhí)行的任務(wù)并追加到適當(dāng)?shù)?Dispatch Queue中,GCD就能生成必要的線(xiàn)程并計(jì)劃執(zhí)行任務(wù)

熟悉了解一下NSThread
  • 特點(diǎn):
    • 1)使用更加面向?qū)ο?/li>
    • 2)簡(jiǎn)單易用,可直接操作線(xiàn)程對(duì)象

- (void)test_NSThread
{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"1)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
    
    NSThread  *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run0) object:nil];
    [thread start]; //必須手動(dòng)開(kāi)啟
    NSLog(@"2)是否是主線(xiàn)程 %d - %@",[thread isMainThread],thread);

    
    //創(chuàng)建線(xiàn)程后自動(dòng)啟動(dòng)線(xiàn)程
    [NSThread detachNewThreadSelector:@selector(run1) toTarget:self withObject:nil];
    
    //隱式創(chuàng)建并啟動(dòng)線(xiàn)程
    [self performSelectorInBackground:@selector(run2) withObject:nil];


    //執(zhí)行后臺(tái)程序
    [self performSelectorInBackground:@selector(doWork) withObject:nil];
}

- (void)run0
{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"3)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
    
}

- (void)run1
{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"4)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
}

- (void)run2
{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"5)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
}

- (void)doWork
{
    sleep(5);
    //長(zhǎng)時(shí)間處理結(jié)束,主線(xiàn)成使用其他處理
    [self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO];
    
    
}

- (void)doneWork
{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"6)是否是主線(xiàn)程 %d - %@",[currentThread isMainThread],currentThread);
}

輸出

2019-01-15 13:30:02.439991+0800 GCD[1211:22656] 1)是否是主線(xiàn)程 1 - <NSThread: 0x6000034aa900>{number = 1, name = main}
2019-01-15 13:30:02.440236+0800 GCD[1211:22656] 2)是否是主線(xiàn)程 0 - <NSThread: 0x6000034cca80>{number = 3, name = main}
2019-01-15 13:30:02.441207+0800 GCD[1211:22695] 4)是否是主線(xiàn)程 0 - <NSThread: 0x6000034cc940>{number = 4, name = (null)}
2019-01-15 13:30:02.442023+0800 GCD[1211:22694] 3)是否是主線(xiàn)程 0 - <NSThread: 0x6000034cca80>{number = 3, name = (null)}
2019-01-15 13:30:02.444018+0800 GCD[1211:22696] 5)是否是主線(xiàn)程 0 - <NSThread: 0x6000034ccac0>{number = 5, name = (null)}
2019-01-15 13:30:07.470940+0800 GCD[1211:22656] 6)是否是主線(xiàn)程 1 - <NSThread: 0x6000034aa900>{number = 1, name = main}

由于使用多線(xiàn)程的程序可以在某個(gè)線(xiàn)程和其他線(xiàn)程之間反復(fù)多次進(jìn)行上下文切換,因此,看上去就像一個(gè)cpu核能夠并發(fā)的執(zhí)行多個(gè)線(xiàn)程一樣;

但是,多線(xiàn)程編程實(shí)際上是一種易發(fā)生各種問(wèn)題的編程技術(shù)。比如 多個(gè)線(xiàn)程更新相同的資源會(huì)導(dǎo)致數(shù)據(jù)不一樣(數(shù)據(jù)競(jìng)爭(zhēng))、停止等待事件的線(xiàn)程會(huì)導(dǎo)致多個(gè)線(xiàn)程互相持續(xù)等待(死鎖)、使用太多線(xiàn)程會(huì)消耗大量的內(nèi)存

應(yīng)用程序在啟動(dòng)的時(shí)候,通過(guò)最先執(zhí)行的線(xiàn)程,即主線(xiàn)程來(lái)描繪用戶(hù)界面、處理觸摸屏幕的事件等等。如果在該主線(xiàn)程中進(jìn)行長(zhǎng)時(shí)間的處理,比如:圖像識(shí)別、數(shù)據(jù)庫(kù)訪(fǎng)問(wèn) 就會(huì)妨礙主線(xiàn)程的執(zhí)行(阻塞)。
會(huì)妨礙主線(xiàn)程中被稱(chēng)為RunLoop的主循環(huán)的執(zhí)行,從而導(dǎo)致不能更新用戶(hù)界面,應(yīng)用程序的畫(huà)面長(zhǎng)時(shí)間停滯等問(wèn)題
使用多線(xiàn)程編程,在執(zhí)行長(zhǎng)時(shí)間的處理時(shí)可以保證用戶(hù)界面的響應(yīng)性能

GCD的API

開(kāi)發(fā)者只需要定義想要執(zhí)行的任務(wù)并追加到適當(dāng)?shù)?Dispatch Queue中,GCD就能生成必要的線(xiàn)程并計(jì)劃執(zhí)行任務(wù)

    dispatch_async(queue , ^{
        //想要執(zhí)行的任務(wù)
    });
  • dispatch queue 如其名所示,是執(zhí)行處理的等待隊(duì)列
    執(zhí)行處理的等待隊(duì)列 分為2種:
dispatch queue種類(lèi) 說(shuō)明
serial Dispatch Queue 等待現(xiàn)在執(zhí)行中處理結(jié)束
Concurrent Dispatch Queue 不等待現(xiàn)在執(zhí)行中處理結(jié)束
dispatch queue 創(chuàng)建

代碼查看

    //dispatch_queue_t dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);
    //參數(shù)一:指定SerialDispatchQueue 的名稱(chēng),可以為NULL
    //參數(shù)二:生成SerialDispatchQueue 可以指定為NULL,生成Concurrent Dispatch Queue 指定為:DISPATCH_QUEUE_CONCURRENT
    dispatch_queue_t mySerialDispatchQueue  = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", NULL);
    
    dispatch_queue_t my ConcurrentlDispatchQueue  = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", DISPATCH_QUEUE_CONCURRENT);

- (void)serialDispatch
{
    NSLog(@"主線(xiàn)程----%@",[NSThread mainThread]);
    
    //參數(shù)一:指定SerialDispatchQueue 的名稱(chēng),可以為NULL
    //參數(shù)二:生成SerialDispatchQueue 可以指定為NULL,生成Concurrent Dispatch Queue 指定為:DISPATCH_QUEUE_CONCURRENT
    dispatch_queue_t mySerialDispatchQueue  = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", NULL);
    
    //向隊(duì)列中添加任務(wù)
    //在block語(yǔ)法中記述想執(zhí)行的處理并將其追加到dispatch Queue中
    dispatch_async(mySerialDispatchQueue, ^{
        NSLog(@"下載圖片1----%@",[NSThread currentThread]);
    });
    
    dispatch_async(mySerialDispatchQueue, ^{
        NSLog(@"下載圖片2----%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(mySerialDispatchQueue, ^{
        NSLog(@"下載圖片3----%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(mySerialDispatchQueue, ^{
        NSLog(@"下載圖片4----%@",[NSThread currentThread]);
    });
    
    //必須要手動(dòng)釋放
    //名稱(chēng)中含有“release”的api在不需要生成的對(duì)象時(shí),有必要通過(guò)dispatch_release函數(shù)進(jìn)行釋放
    //如果你部署的最低目標(biāo)是 iOS 6.0 or Mac OS X 10.8 或者更高的
    //ARC已經(jīng)能夠管理GCD對(duì)象了,這時(shí)候,GCD對(duì)象就如同普通的OC對(duì)象一樣,不應(yīng)該使用dispatch_retain ordispatch_release
    //dispatch_release(mySerialDispatchQueue);
}

打印

2019-01-15 13:56:47.280855+0800 GCD[1434:32459] 主線(xiàn)程----<NSThread: 0x600001b31400>{number = 1, name = main}
2019-01-15 13:56:47.281106+0800 GCD[1434:32500] 下載圖片1----<NSThread: 0x600001b6a940>{number = 3, name = (null)}
2019-01-15 13:56:47.281242+0800 GCD[1434:32500] 下載圖片2----<NSThread: 0x600001b6a940>{number = 3, name = (null)}
2019-01-15 13:56:47.281375+0800 GCD[1434:32500] 下載圖片3----<NSThread: 0x600001b6a940>{number = 3, name = (null)}
2019-01-15 13:56:47.281511+0800 GCD[1434:32500] 下載圖片4----<NSThread: 0x600001b6a940>{number = 3, name =

總結(jié):serial Dispatch Queue (等待現(xiàn)在執(zhí)行中處理結(jié)束),使用的是一個(gè)線(xiàn)程,一個(gè)一個(gè)執(zhí)行

當(dāng)隊(duì)列改為Concurrent Dispatch Queue,

    //參數(shù)一:指定SerialDispatchQueue 的名稱(chēng),可以為NULL
    //參數(shù)二:生成SerialDispatchQueue 可以指定為NULL,生成Concurrent Dispatch Queue 指定為:DISPATCH_QUEUE_CONCURRENT
    dispatch_queue_t mySerialDispatchQueue  = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", DISPATCH_QUEUE_CONCURRENT);

輸出如下:

2019-01-15 14:03:17.159117+0800 GCD[1514:35799] 主線(xiàn)程----<NSThread: 0x6000021be940>{number = 1, name = main}
2019-01-15 14:03:17.159437+0800 GCD[1514:35836] 下載圖片1----<NSThread: 0x6000021edb40>{number = 3, name = (null)}
2019-01-15 14:03:17.159444+0800 GCD[1514:35833] 下載圖片4----<NSThread: 0x6000021edc00>{number = 6, name = (null)}
2019-01-15 14:03:17.159463+0800 GCD[1514:35835] 下載圖片2----<NSThread: 0x6000021edbc0>{number = 5, name = (null)}
2019-01-15 14:03:17.159467+0800 GCD[1514:35834] 下載圖片3----<NSThread: 0x6000021edb00>{number = 4, name = (null)}
2019-01-15 14:03:17.159595+0800 GCD[1514:35836] 下載圖片5----<NSThread: 0x6000021edb40>{number = 3, name = (null)}
2019-01-15 14:03:17.159623+0800 GCD[1514:35833] 下載圖片6----<NSThread: 0x6000021edc00>{number = 6, name = (null)}
2019-01-15 14:03:17.159681+0800 GCD[1514:35835] 下載圖片7----<NSThread: 0x6000021edbc0>{number = 5, name = (null)}
2019-01-15 14:03:17.159704+0800 GCD[1514:35834] 下載圖片8----<NSThread: 0x6000021edb00>{number = 4, name = (null)}

總結(jié):Concurrent Dispatch Queue,不等待現(xiàn)在執(zhí)行中處理結(jié)束,使用的是多個(gè)線(xiàn)程,并發(fā)的執(zhí)行

注意:
雖然一個(gè)serial Dispatch Queue 同時(shí)只能執(zhí)行一個(gè)追加處理(一個(gè)線(xiàn)程),如果多個(gè)serial Dispatch Queue(多個(gè)線(xiàn)程) 將并發(fā)執(zhí)行【前提是沒(méi)有改變優(yōu)先級(jí),代碼使用dispatch_set_target_queue 有介紹】
serial Dispatch Queue 使用一個(gè)線(xiàn)程時(shí),數(shù)據(jù)是安全的

/**
 * 總結(jié):會(huì)開(kāi)啟線(xiàn)程,但是只開(kāi)啟一個(gè)線(xiàn)程,同步執(zhí)行
 * 當(dāng)生成多個(gè)serial Dispatch Queue時(shí),各個(gè)serial Dispatch Queue將并行執(zhí)行
 */
- (void)serialDispatch02
{
    NSLog(@"主線(xiàn)程----%@",[NSThread mainThread]);
    
    //參數(shù)一:指定SerialDispatchQueue 的名稱(chēng),可以為NULL
    //參數(shù)二:生成SerialDispatchQueue 可以指定為NULL,生成Concurrent Dispatch Queue 指定為:DISPATCH_QUEUE_CONCURRENT
    dispatch_queue_t mySerialDispatchQueue  = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", NULL);
    dispatch_queue_t mySerialDispatchQueue2  = dispatch_queue_create("等待現(xiàn)在執(zhí)行中處理結(jié)束", NULL);
    
    dispatch_async(mySerialDispatchQueue, ^{
        NSLog(@"下載圖片1----%@",[NSThread currentThread]);
    });
    
    dispatch_async(mySerialDispatchQueue2, ^{
        NSLog(@"下載圖片2----%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(mySerialDispatchQueue2, ^{
        NSLog(@"下載圖片3----%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(mySerialDispatchQueue, ^{
        NSLog(@"下載圖片4----%@",[NSThread currentThread]);
        
    });
    
}

輸出

2019-01-15 14:11:00.976494+0800 GCD[1579:38823] 主線(xiàn)程----<NSThread: 0x60000027d400>{number = 1, name = main}
2019-01-15 14:11:00.976780+0800 GCD[1579:38869] 下載圖片1----<NSThread: 0x600000229640>{number = 4, name = (null)}
2019-01-15 14:11:00.976780+0800 GCD[1579:38871] 下載圖片2----<NSThread: 0x600000216540>{number = 3, name = (null)}
2019-01-15 14:11:00.976956+0800 GCD[1579:38871] 下載圖片3----<NSThread: 0x600000216540>{number = 3, name = (null)}
2019-01-15 14:11:00.977072+0800 GCD[1579:38869] 下載圖片4----<NSThread: 0x600000229640>{number = 4, name = (null)}
全局隊(duì)列queue、主隊(duì)列queue

Main Dispatch Queue 在主線(xiàn)程執(zhí)行的Dispatch Queue,因?yàn)橹鳜F(xiàn)場(chǎng)只有一個(gè),所以Main Dispatch Queue 自然就是serialDispatch queue,串行執(zhí)行,

- (void)mainDispatchQueue
{
    dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

    dispatch_async(mainDispatchQueue, ^{
        NSLog(@"在mainDispatchQueue 里面,當(dāng)前現(xiàn)場(chǎng)---%@",[NSThread currentThread]);
        NSLog(@"在mainDispatchQueue 里面,主線(xiàn)程----%@",[NSThread mainThread]);
        
    });
}

輸出如下: 都是主線(xiàn)程

2019-01-15 14:21:18.661956+0800 GCDDemo01[1682:42652] 在mainDispatchQueue 里面,當(dāng)前現(xiàn)場(chǎng)---<NSThread: 0x600000cd9400>{number = 1, name = main}
2019-01-15 14:21:18.662125+0800 GCDDemo01[1682:42652] 在mainDispatchQueue 里面,主線(xiàn)程----<NSThread: 0x600000cd9400>{number = 1, name = main}

全局隊(duì)列 Global Dispatch Queu:block塊里面是并行執(zhí)行的

4個(gè)優(yōu)先級(jí) 如下:

Global Dispatch Queue種類(lèi) 說(shuō)明
DISPATCH_QUEUE_PRIORITY_HIGH 執(zhí)行優(yōu)先級(jí): 最高
DISPATCH_QUEUE_PRIORITY_DEFAULT 執(zhí)行優(yōu)先級(jí): 默認(rèn)
DISPATCH_QUEUE_PRIORITY_LOW 執(zhí)行優(yōu)先級(jí): 低
DISPATCH_QUEUE_PRIORITY_BACKGROUND 執(zhí)行優(yōu)先級(jí): 后臺(tái)

創(chuàng)建方法

    //第二個(gè)參數(shù) 都是0
    dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    
    dispatch_async(globalDispatchQueue, ^{
        NSLog(@"當(dāng)前現(xiàn)場(chǎng)---%@",[NSThread currentThread]);
        NSLog(@"主線(xiàn)程----%@",[NSThread mainThread]);

        //可并行執(zhí)行的處理
        //...
        
        dispatch_async(mainDispatchQueue, ^{
            //只能在主線(xiàn)程中執(zhí)行的處理
            //...
            [self.btn setTitle:@"load..." forState:UIControlStateNormal];
        });
    });

打印如下:

2019-01-15 14:32:22.606748+0800 GCDDemo01[1752:46356] 當(dāng)前現(xiàn)場(chǎng)---<NSThread: 0x600000bc2600>{number = 3, name = (null)}
2019-01-15 14:32:22.607040+0800 GCDDemo01[1752:46356] 主線(xiàn)程----<NSThread: 0x600000ba6940>{number = 1, name = (null)}
變更優(yōu)先級(jí) dispatch_set_target_queue

void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t _Nullable queue);

第一個(gè)參數(shù)為要設(shè)置優(yōu)先級(jí)的queue,第二個(gè)參數(shù)是參照物,既將第一個(gè)queue的優(yōu)先級(jí)和第二個(gè)queue的優(yōu)先級(jí)設(shè)置一樣。

dispatch_queue_create函數(shù)生成的DisPatch Queue不管是Serial DisPatch Queue還是Concurrent Dispatch Queue,執(zhí)行的優(yōu)先級(jí)都與默認(rèn)優(yōu)先級(jí)的Global Dispatch queue相同,
如果需要變更生成的Dispatch Queue的執(zhí)行優(yōu)先級(jí)則需要使用dispatch_set_target_queue函數(shù)

  • 變更優(yōu)先級(jí)
    //串行隊(duì)列
    dispatch_queue_t serialDiapatchQueue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_SERIAL);
      
     //全局隊(duì)列
    dispatch_queue_t dispatchgetglobalqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    
    //使串行隊(duì)列 與全局隊(duì)列優(yōu)先級(jí)low 相同
    dispatch_set_target_queue(serialDiapatchQueue, dispatchgetglobalqueue);
    
    
    dispatch_async(serialDiapatchQueue, ^{
        NSLog(@"我優(yōu)先級(jí)低,先讓讓");
    });
    
    //   serialDiapatchQueue  優(yōu)先級(jí)< 新的全局隊(duì)列

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"我優(yōu)先級(jí)高,我先block");
    });
    

打印

2019-01-15 14:49:46.603854+0800 GCDDemo01[1874:51890] 我優(yōu)先級(jí)高,我先block
2019-01-15 14:49:46.603884+0800 GCDDemo01[1874:51891] 我優(yōu)先級(jí)低,先讓讓
  • 使用dispatch_set_target_queue將多個(gè)串行的queue指定到了同一目標(biāo),那么著多個(gè)串行queue在目標(biāo)queue上就是同步執(zhí)行的,不再是并行執(zhí)行。
 //1.創(chuàng)建目標(biāo)隊(duì)列
    dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
    
    //2.創(chuàng)建3個(gè)串行隊(duì)列
    dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
    
    //3.將3個(gè)串行隊(duì)列分別添加到目標(biāo)隊(duì)列
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    dispatch_set_target_queue(queue3, targetQueue);
    
    
    dispatch_async(queue1, ^{
        NSLog(@"1 in");
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"1 out");
    });
    
    dispatch_async(queue2, ^{
        NSLog(@"2 in");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"2 out");
    });
    dispatch_async(queue3, ^{
        NSLog(@"3 in");
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"3 out");
    });

打印如下: 不再是并行執(zhí)行。

2019-01-15 14:58:52.260714+0800 GCDDemo01[1945:55274] 1 in
2019-01-15 14:58:55.266195+0800 GCDDemo01[1945:55274] 1 out
2019-01-15 14:58:55.266507+0800 GCDDemo01[1945:55274] 2 in
2019-01-15 14:58:57.267504+0800 GCDDemo01[1945:55274] 2 out
2019-01-15 14:58:57.267880+0800 GCDDemo01[1945:55274] 3 in
2019-01-15 14:58:58.273349+0800 GCDDemo01[1945:55274] 3 out

Serial DisPatch Queue是一個(gè)串行隊(duì)列,只能同時(shí)執(zhí)行1個(gè)追加處理(即任務(wù)),當(dāng)用Dispatch_queue_create函數(shù)生成多個(gè)Serial DisPatch Queue時(shí),每個(gè)Serial DisPatch Queue均獲得一個(gè)線(xiàn)程,即多個(gè)Serial DisPatch Queue可并發(fā)執(zhí)行,同時(shí)處理添加到各個(gè)Serial DisPatch Queue中的任務(wù)

但要注意如果過(guò)多地使用多線(xiàn)程,就會(huì)消耗大量?jī)?nèi)存,引起大量的上下文切換,大幅度降低系統(tǒng)的響應(yīng)性能,所以我們只在為了避免多個(gè)線(xiàn)程更新相同資源導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)時(shí),使用Serial DisPatch Queue。

dispatch_after
- (void)dispatchAfter
{
    NSLog(@"開(kāi)始調(diào)用");
    //DISPATCH_TIME_NOW: 表示現(xiàn)在的時(shí)間
    //ull:是C語(yǔ)言的數(shù)值字面量,是顯式表示類(lèi)型時(shí)使用的字符串(表示“unsigned long long”)
    //NSEC_PER_MSEC: 表示可以以毫秒為單位計(jì)算
    //
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
    
    //在3s后用dispatch_async 函數(shù)追加block到main dispatch Queue
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"延遲調(diào)用");
    });
    
    //dispatch_walltime 函數(shù)用于計(jì)算絕對(duì)時(shí)間
    //dispatch_time 函數(shù)用于計(jì)算相對(duì)時(shí)間
    //dispatch_walltime(<#const struct timespec * _Nullable when#>, <#int64_t delta#>)
}
dispatch_Group

在開(kāi)發(fā)過(guò)程中,在追加到dispathc Queue 中的多個(gè)處理全部結(jié)束后想執(zhí)行結(jié)束處理,這種情況經(jīng)常出現(xiàn)。那么可以引入dispatch_Group

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    //與dispatch_asyn 不同的是指定生成的dispatch_group_t 為第一參數(shù)。指定的block屬于指定的group
    dispatch_group_async(group, queue, ^{
        NSLog(@"組一   %@",[NSThread currentThread] );
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"組二   %@",[NSThread currentThread] );
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"組三   %@",[NSThread currentThread] );
        sleep(10);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"執(zhí)行完畢   %@",[NSThread currentThread] );
    });
    
    
    //第二參數(shù):指定為等待的時(shí)間(超時(shí))
    //DISPATCH_TIME_FOREVER  永久等待,只要屬于group 的處理尚未執(zhí)行介紹,就會(huì)一直等待,中途不能取消
    //DISPATCH_TIME_NOW 不等待即可判斷屬于group 的處理是否執(zhí)行結(jié)束
    //long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
    long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    if (result == 0) {
        NSLog(@"屬于group 的全部處理執(zhí)行結(jié)束");
    } else {
        NSLog(@"屬于group 的某一個(gè)處理還在執(zhí)行");
    }
    
    NSLog(@"主線(xiàn)程----%@",[NSThread mainThread]);

打印數(shù)據(jù)如下:

2019-01-15 15:32:28.715196+0800 GCDDemo01[2208:65352] 組三   <NSThread: 0x6000032872c0>{number = 5, name = (null)}
2019-01-15 15:32:28.715196+0800 GCDDemo01[2208:65354] 組二   <NSThread: 0x600003287200>{number = 4, name = (null)}
2019-01-15 15:32:28.715196+0800 GCDDemo01[2208:65353] 組一   <NSThread: 0x6000032bce40>{number = 3, name = (null)}
2019-01-15 15:32:38.716752+0800 GCDDemo01[2208:65320] 屬于group 的全部處理執(zhí)行結(jié)束
2019-01-15 15:32:38.716945+0800 GCDDemo01[2208:65320] 主線(xiàn)程----<NSThread: 0x6000032d9400>{number = 1, name = main}
2019-01-15 15:32:38.747027+0800 GCDDemo01[2208:65320] 執(zhí)行完畢   <NSThread: 0x6000032d9400>{number = 1, name = main}

那么這個(gè)能夠解決在開(kāi)發(fā)過(guò)程中,一個(gè)控制器里面,有2個(gè)異步網(wǎng)絡(luò)請(qǐng)求,當(dāng)所有的請(qǐng)求結(jié)束后刷新頁(yè)面呢?
如下:

- (void)dispatchGroupDemo
{
    NSURLSession *session = [NSURLSession sharedSession];
    
    dispatch_queue_t dispatchQueue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t dispatchGroup = dispatch_group_create();
   
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        
        NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSLog(@"got data from internet1");
        }];
        [task resume];
    });
    
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSLog(@"got data from internet2");
        }];
        [task resume];
    });
    
    dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
        NSLog(@"end");
    });
    
    /**
     上述輸出結(jié)果:顯然沒(méi)有的達(dá)到要求
     這是因?yàn)檫@里的網(wǎng)絡(luò)請(qǐng)求是個(gè)異步的方法,沒(méi)有等待具體的數(shù)據(jù)返回,放入的dispatch queue的 block就執(zhí)行完畢了。所以沒(méi)收到2個(gè)網(wǎng)絡(luò)數(shù)據(jù),就提前調(diào)用了dispatch_group_notify指定的結(jié)束方法。
     
     2018-08-21 17:36:45.729321+0800 GCDDemo01[97318:3993362] end
     2018-08-21 17:36:45.832798+0800 GCDDemo01[97318:3993495] got data from internet2
     2018-08-21 17:36:45.870109+0800 GCDDemo01[97318:3993455] got data from internet1
     */

}

很明顯,這個(gè)方法行不通, 這是因?yàn)檫@里的網(wǎng)絡(luò)請(qǐng)求是個(gè)異步的方法,沒(méi)有等待具體的數(shù)據(jù)返回,放入的dispatch queue的 block就執(zhí)行完畢了。所以沒(méi)收到2個(gè)網(wǎng)絡(luò)數(shù)據(jù),就提前調(diào)用了dispatch_group_notify指定的結(jié)束方法。

下面這個(gè)勉強(qiáng)能行

- (void)dispatchGroupDemo02
{
    NSURLSession *session = [NSURLSession sharedSession];
    dispatch_group_t dispatchGroup = dispatch_group_create();
   
    
    //手動(dòng)指示一個(gè)block塊已進(jìn)入組
    //調(diào)用此函數(shù)表示另一個(gè)block塊已通過(guò)除 dispatch_group_async() 之外的其他方法加入該組。對(duì)這個(gè)函數(shù)的調(diào)用必須與dispatch_group_leave()進(jìn)行平衡。
    dispatch_group_enter(dispatchGroup);
    NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"got data from internet1");
        
        dispatch_group_leave(dispatchGroup);
    }];
    [task resume];
    
    
    dispatch_group_enter(dispatchGroup);
    NSURLSessionDataTask *task2 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"got data from internet2");
        
        dispatch_group_leave(dispatchGroup);
    }];
    [task2 resume];
    
    dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
        NSLog(@"end");
    });
    
    /**
     輸出結(jié)果:
     2018-08-21 17:41:02.463886+0800 GCDDemo01[97790:4012417] got data from internet1
     2018-08-21 17:41:02.493294+0800 GCDDemo01[97790:4012421] got data from internet2
     2018-08-21 17:41:02.493488+0800 GCDDemo01[97790:4012365] end
     
     相對(duì)于簡(jiǎn)單的dispatch_group_async,dispatch_group_enter 和 dispatch_group_leave 可以對(duì)group進(jìn)行更細(xì)致的處理。
     
     dispatch_group_enter:(上面已有解釋)
     簡(jiǎn)單的說(shuō),就是dispatch_group_enter會(huì)對(duì)group的內(nèi)部計(jì)數(shù)加一,dispatch_group_leave會(huì)對(duì)group的內(nèi)部計(jì)數(shù)減一,就類(lèi)似以前的retain和release方法。說(shuō)白了也是維護(hù)了一個(gè)計(jì)數(shù)器。
     
     以前我的做法就是自己維護(hù)計(jì)數(shù)器。在發(fā)送網(wǎng)絡(luò)請(qǐng)求前,記下發(fā)送總數(shù),數(shù)據(jù)返回后,在同一個(gè)thread中(或者在一個(gè)DISPATCH_QUEUE_SERIAL類(lèi)型的dispatch_queue中),對(duì)計(jì)數(shù)器進(jìn)行+1操作,當(dāng)計(jì)數(shù)器和網(wǎng)絡(luò)請(qǐng)求數(shù)相等時(shí),調(diào)用最后的處理。
     
     相比自己的處理的計(jì)數(shù)器,dispatch_group_enter 處理方法可能顯得更正規(guī)一些,代碼更規(guī)范了,但執(zhí)行效果是一樣的。。。
     
     
     
     今天再改其他的工程的時(shí)候,又遇到了這個(gè)問(wèn)題,有一個(gè)值,需要2個(gè)異步操作查詢(xún)回2個(gè)值進(jìn)行計(jì)算,因此必須再2個(gè)異步操作結(jié)束后才能進(jìn)行計(jì)算操作。開(kāi)始試著使用了OperationQueue,想用addDependency方法,但是這個(gè)方法無(wú)法靈活地控制,只適合block內(nèi)容已經(jīng)確定的情況。對(duì)于我遇到的這種異步操作,block的內(nèi)容是不定的,需要依賴(lài)異步的返回,用operation queue會(huì)遇到各種問(wèn)題,無(wú)法解決問(wèn)題,十分復(fù)雜!
     
     */
}

dispatch_barrier_async

在訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)或文件時(shí),使用同步隊(duì)列Serial dispatch queue 可避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,
寫(xiě)入處理確實(shí)不可與其他寫(xiě)入處理以及包含讀取處理的其他某些處理并行執(zhí)行。但是如果讀取處理只是與讀取粗糲并行執(zhí)行,這是不會(huì)有問(wèn)題的;
為了高校的訪(fǎng)問(wèn),讀取處理追加到 并行隊(duì)列Concurrent dispatch queue 中,寫(xiě)入處理在任意一個(gè)讀取處理沒(méi)有執(zhí)行的狀態(tài)下,追加到Serial dispatch queue 中即可(寫(xiě)入處理之前,讀取處理不可執(zhí)行)

為了解決這個(gè)問(wèn)題,引入:dispatch_barrier_async

   dispatch_queue_t queue = dispatch_queue_create("label1", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"異步一 %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"異步二 %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"異步三 %@",[NSThread currentThread]);
    });
    
    
    //dispatch_barrier_async函數(shù):
    //會(huì)等待追加到 并行隊(duì)列queue 上的并行執(zhí)行的處理全部結(jié)束以后,再將指定的處理追加到該 并行隊(duì)列queue 中,
    //然后再由dispatch_barrier_async函數(shù)追加的處理執(zhí)行完畢后, 并行隊(duì)列queue 才恢復(fù)為一般的動(dòng)作
    //簡(jiǎn)單地說(shuō),就是在這個(gè)函數(shù)之前被提交到quque里的block一定會(huì)被先執(zhí)行,之后執(zhí)行dispatch_barrier_async設(shè)定的block,最后執(zhí)行調(diào)用dispatch_barrier_async之后才提交到queue里的block。
    dispatch_barrier_async(queue, ^{
        NSLog(@"dispatch_barrier_async");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"異步四 %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"異步五 %@",[NSThread currentThread]);
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"異步六 %@",[NSThread currentThread]);
    });

打印如下

2019-01-15 16:00:16.373259+0800 GCDDemo01[2440:75032] 異步一 <NSThread: 0x600003f4edc0>{number = 3, name = (null)}
2019-01-15 16:00:16.373259+0800 GCDDemo01[2440:75031] 異步三 <NSThread: 0x600003f70f40>{number = 5, name = (null)}
2019-01-15 16:00:16.373261+0800 GCDDemo01[2440:75033] 異步二 <NSThread: 0x600003f4f140>{number = 4, name = (null)}
2019-01-15 16:00:16.373659+0800 GCDDemo01[2440:75032] dispatch_barrier_async
2019-01-15 16:00:16.373819+0800 GCDDemo01[2440:75031] 異步五 <NSThread: 0x600003f70f40>{number = 5, name = (null)}
2019-01-15 16:00:16.373840+0800 GCDDemo01[2440:75032] 異步四 <NSThread: 0x600003f4edc0>{number = 3, name = (null)}
2019-01-15 16:00:16.373819+0800 GCDDemo01[2440:75033] 異步六 <NSThread: 0x600003f4f140>{number = 4, name = (null)}

dispatch_barrier_async函數(shù):
1)會(huì)等待追加到 并行隊(duì)列queue 上的并行執(zhí)行的處理全部結(jié)束以后,再將指定的處理追加到該 并行隊(duì)列queue 中,
2)然后再由dispatch_barrier_async函數(shù)追加的處理執(zhí)行完畢后, 并行隊(duì)列queue 才恢復(fù)為一般的動(dòng)作
3)簡(jiǎn)單地說(shuō),就是在這個(gè)函數(shù)之前被提交到quque里的block一定會(huì)被先執(zhí)行,之后執(zhí)行dispatch_barrier_async設(shè)定的block,最后執(zhí)行調(diào)用dispatch_barrier_async之后才提交到queue里的block。

dispatch_sync

同步:

 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //一旦調(diào)用dispatch_sync函數(shù),那么指定的處理執(zhí)行結(jié)束之前,該函數(shù)不會(huì)返回(返回的意思就是從隊(duì)列你們出來(lái))
    dispatch_sync(globalQueue, ^{
        NSLog(@"同步操作 并行隊(duì)列1  %@",[NSThread currentThread]);
    });
    
    dispatch_sync(globalQueue, ^{
        NSLog(@"同步操作 并行隊(duì)列2  %@",[NSThread currentThread]);
    });
    
    dispatch_sync(globalQueue, ^{
        NSLog(@"同步操作 并行隊(duì)列3  %@",[NSThread currentThread]);
    });
    
    dispatch_sync(globalQueue, ^{
        NSLog(@"同步操作 并行隊(duì)列4  %@",[NSThread currentThread]);
    });
    
    NSLog(@"同步操作 并行隊(duì)列5  %@",[NSThread currentThread]);

打?。?都在主線(xiàn)程

2019-01-15 16:19:55.460438+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列1  <NSThread: 0x600000266e80>{number = 1, name = main}
2019-01-15 16:19:55.460683+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列2  <NSThread: 0x600000266e80>{number = 1, name = main}
2019-01-15 16:19:55.460814+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列3  <NSThread: 0x600000266e80>{number = 1, name = main}
2019-01-15 16:19:55.460953+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列4  <NSThread: 0x600000266e80>{number = 1, name = main}
2019-01-15 16:19:55.461079+0800 GCDDemo01[2669:83775] 同步操作 并行隊(duì)列5  <NSThread: 0x600000266e80>{number = 1, name = main}

為什么都是在主線(xiàn)程里面執(zhí)行?

這是一個(gè) 同步函數(shù)+并發(fā)隊(duì)列
這些任務(wù)都是創(chuàng)建一個(gè)就立馬執(zhí)行,執(zhí)行完才創(chuàng)建下一個(gè),因?yàn)槭峭胶瘮?shù),所以不新建線(xiàn)程,只要是同步函數(shù),就不會(huì)新建線(xiàn)程。
并發(fā)隊(duì)列與否,并不影響同步函數(shù)的創(chuàng)建,因?yàn)楸旧砭筒荒芏鄤?chuàng)建線(xiàn)程,也就不存在并發(fā)

容易造成死鎖

 //同步函數(shù)+串隊(duì)列
    NSLog(@"死鎖問(wèn)題輸出01  %@",[NSThread currentThread]);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{ //崩潰到這一步
        NSLog(@"死鎖問(wèn)題輸出02  %@",[NSThread currentThread]);
    });
    NSLog(@"死鎖問(wèn)題輸出03  %@",[NSThread currentThread]);

原因:(查看很多博客,五花八門(mén),解釋的很多不一樣,但總的一點(diǎn)就是互相阻塞)
下面是我總結(jié)的一個(gè)說(shuō)法:

 總結(jié):
 一旦調(diào)用dispatch_sync函數(shù),那么指定的處理執(zhí)行結(jié)束之前,該函數(shù)不會(huì)返回(返回的意思就是從隊(duì)列你們出來(lái))
 相反dispatch_async這個(gè)函數(shù)是開(kāi)一條線(xiàn)程出來(lái)執(zhí)行Block,不等Block返回就直接返回了。

 我們通常把任務(wù)也就是所說(shuō)的block塊(swift 叫閉包)交給GCD函數(shù),函數(shù)把任務(wù)添加到隊(duì)列queue(先進(jìn)先出的原則)里面
 隊(duì)列遵循嚴(yán)格的先進(jìn)后出的原則,同一個(gè)queue中,最早入列的block,會(huì)最早的分配給線(xiàn)程去執(zhí)行
 
 GCD隊(duì)列只是組織待執(zhí)行任務(wù)的一個(gè)數(shù)據(jù)結(jié)構(gòu)封裝,而線(xiàn)程才是執(zhí)行任務(wù)的人
 
 串行隊(duì)列:隊(duì)列中的任務(wù)是一個(gè)執(zhí)行完成后才去執(zhí)行另一個(gè)
 解釋?zhuān)? 1)主線(xiàn)程調(diào)用dispatch_sync這個(gè)函數(shù)               【這一步把dispatch_sync 函數(shù)本身 進(jìn)入main隊(duì)列,記做A任務(wù)】
 2)當(dāng)這個(gè)函數(shù)返回(執(zhí)行完畢)的時(shí)候主線(xiàn)程才能往下執(zhí)行  【dispatch_sync 函數(shù)執(zhí)行完畢才能出main隊(duì)列,才能繼續(xù)往下執(zhí)行,執(zhí)行B任務(wù),也就是block塊】
 3)但dispatch_sync返回的條件是里面的Block返回,里面的Block是不會(huì)執(zhí)行的,因?yàn)樗潜徊宓街麝?duì)列最后執(zhí)行,然而因?yàn)閐ispatch_sync無(wú)法返回,所以主隊(duì)列無(wú)法執(zhí)行到最后一個(gè)任務(wù)。                      【執(zhí)行block任務(wù),記做B任務(wù),它在任務(wù)A后面,必須等待任務(wù)A執(zhí)行完畢,才能執(zhí)行B】

 【A任務(wù) 等待B任務(wù)執(zhí)行結(jié)束后才能出main隊(duì)列,但是串行main隊(duì)列中(同步函數(shù)),B任務(wù)在A任務(wù)后面,A任務(wù)不完成,是不會(huì)執(zhí)行B任務(wù)的,從而造成互相阻塞,從而死鎖】
 
 如果隊(duì)列 改為 自行創(chuàng)建的串行隊(duì)列 dispatch_queue_create("d", NULL),不會(huì)造成死鎖,為什么?

 因?yàn)椋?block塊添加的隊(duì)列 是自行創(chuàng)建的隊(duì)列,跟dispatch_sync函數(shù)添加到的mian隊(duì)列不是同一個(gè),blocl塊添加到隊(duì)列里,是隊(duì)列的頭也是尾,所以先執(zhí)行并返回,然后dispatch_sync返回,繼續(xù)執(zhí)行dispatch_sync函數(shù)下面的語(yǔ)句

圖解:
死鎖分析1.png
死鎖分析2.png
dispatch_apply
  • dispatch_apply 函數(shù)時(shí)dispatch_sync 函數(shù)和dispatch Groupd的關(guān)聯(lián)API
  • dispatch_apply 函數(shù)按指定的次數(shù)將指定的block 追加到指定的dispatch_queue_t 中,并等待全部處理執(zhí)行結(jié)束
    //參數(shù)一: 重復(fù)次數(shù)
    //參數(shù)二:追加對(duì)象的queue
    //參數(shù)三:追加的處理
    dispatch_apply(10, global, ^(size_t index) {
        NSLog(@"%zu",index);
        //sleep(2);
    });
    
    NSLog(@"done");

輸出

2019-01-15 17:05:15.313947+0800 GCDDemo01[2990:98143] 1
2019-01-15 17:05:15.313947+0800 GCDDemo01[2990:98097] 0
2019-01-15 17:05:15.313949+0800 GCDDemo01[2990:98134] 2
2019-01-15 17:05:15.313977+0800 GCDDemo01[2990:98137] 3
2019-01-15 17:05:15.314154+0800 GCDDemo01[2990:98097] 7
2019-01-15 17:05:15.314154+0800 GCDDemo01[2990:98143] 6
2019-01-15 17:05:15.314155+0800 GCDDemo01[2990:98134] 5
2019-01-15 17:05:15.314155+0800 GCDDemo01[2990:98137] 4
2019-01-15 17:05:15.314260+0800 GCDDemo01[2990:98143] 8
2019-01-15 17:05:15.314268+0800 GCDDemo01[2990:98097] 9
2019-01-15 17:05:15.314756+0800 GCDDemo01[2990:98097] done

因?yàn)槿株?duì)列,所以輸出結(jié)果 不確定,但最后 done 輸出必定在最后的位置
這是因?yàn)?code>dispatch_apply函數(shù)會(huì)等待全部處理執(zhí)行結(jié)束

  • 在dispatch_async函數(shù)中異步執(zhí)行dispatch_apply函數(shù),模擬dispatch_sync的同步效果
- (void)dispatch_apply03
{
    //在dispatch_async函數(shù)中異步執(zhí)行dispatch_apply函數(shù),模擬dispatch_sync的同步效果
    NSArray *array = @[@"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", @"j"];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(queue, ^{
        NSLog(@"執(zhí)行一");
        
        //等待dispatch_apply 函數(shù)中的全部處理執(zhí)行結(jié)束
        dispatch_apply([array count], queue, ^(size_t index) {
            
            //處理包含在NArrray對(duì)象的全部對(duì)象
            NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
        });
        
        NSLog(@"執(zhí)行二");
        //在main dispatch queue 中非同步執(zhí)行
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"回到主線(xiàn)程執(zhí)行用戶(hù)界面更新等操作");
        });
        
    });
}
    2019-01-15 17:10:11.744106+0800 GCDDemo01[3034:99956] 執(zhí)行一
     2019-01-15 17:10:11.744316+0800 GCDDemo01[3034:99956] 0: a
     2019-01-15 17:10:11.744419+0800 GCDDemo01[3034:99956] 1: b
     2019-01-15 17:10:11.744516+0800 GCDDemo01[3034:99956] 2: c
     2019-01-15 17:10:11.744601+0800 GCDDemo01[3034:99956] 3: d
     2019-01-15 17:10:11.744695+0800 GCDDemo01[3034:99956] 4: e
     2019-01-15 17:10:11.744778+0800 GCDDemo01[3034:99956] 5: f
     2019-01-15 17:10:11.744881+0800 GCDDemo01[3034:99956] 6: g
     2019-01-15 17:10:11.745772+0800 GCDDemo01[3034:99956] 7: h
     2019-01-15 17:10:11.746091+0800 GCDDemo01[3034:99956] 8: i
     2019-01-15 17:10:11.746418+0800 GCDDemo01[3034:99956] 9: j
     2019-01-15 17:10:11.746750+0800 GCDDemo01[3034:99956] 執(zhí)行二
     2019-01-15 17:10:11.759049+0800 GCDDemo01[3034:99913] 回到主線(xiàn)程執(zhí)行用戶(hù)界面更新等操作
dispatch_suspend

有時(shí)候希望不執(zhí)行已追加的處理 從而引入dispatch_suspend

dispatch_suspend函數(shù): 掛起指定的dispatch_queue
dispatch_resume函數(shù): 恢復(fù)指定的dispatch_queue
注意: 對(duì)已經(jīng)執(zhí)行的處理沒(méi)有影響,掛起后,追加到dispatch_queue 中尚未執(zhí)行處理的,在此之后停止執(zhí)行。而恢復(fù)則使得這些處理能夠繼續(xù)執(zhí)行

 dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
    //提交第一個(gè)block,延時(shí)5秒打印。
    dispatch_async(queue, ^{
        sleep(5);
        NSLog(@"After 5 seconds...");
    });
    
    //提交第二個(gè)block,也是延時(shí)5秒打印
    dispatch_async(queue, ^{
        sleep(5);
        NSLog(@"After 5 seconds again...");
    });
    
    //延時(shí)一秒
    NSLog(@"sleep 1 second...");
    sleep(1);
   
    //掛起隊(duì)列
    NSLog(@"suspend...");
    dispatch_suspend(queue);
    
    //延時(shí)10秒
    NSLog(@"sleep 10 second...");
    sleep(10);
    
    //恢復(fù)隊(duì)列
    NSLog(@"resume...");
    dispatch_resume(queue);

打印如下:

2019-01-15 17:16:51.256678+0800 GCDDemo01[3094:102562] sleep 1 second...
2019-01-15 17:16:52.257143+0800 GCDDemo01[3094:102562] suspend...
2019-01-15 17:16:52.257403+0800 GCDDemo01[3094:102562] sleep 10 second...
2019-01-15 17:16:56.260057+0800 GCDDemo01[3094:102606] After 5 seconds...
2019-01-15 17:17:02.258408+0800 GCDDemo01[3094:102562] resume...
2019-01-15 17:17:07.262380+0800 GCDDemo01[3094:102603] After 5 seconds again...
dispatch_semaphore_t 信號(hào)量

dispatch_semaphore_t 是持有計(jì)數(shù)的信號(hào),該計(jì)數(shù)是多線(xiàn)程編程中的計(jì)數(shù)類(lèi)型信號(hào)。

  • 創(chuàng)建信號(hào)
//參數(shù)表示計(jì)數(shù)的初始值
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
  • 等待信號(hào)量
    【當(dāng)計(jì)數(shù)值>=1,或者在待機(jī)中計(jì)數(shù)值>= 1,對(duì)該計(jì)數(shù)進(jìn)行減法并從dispatch_semaphore_wait函數(shù)返回; 返回值==0時(shí),可以安全的執(zhí)行需要進(jìn)行怕他控制的處理】
//long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

//時(shí)間
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
  • dispatch_semaphore_signal 提高信號(hào)量
dispatch_semaphore_signal(semaphore);
    //計(jì)數(shù)值 ==1
    //保證可訪(fǎng)問(wèn)數(shù)組NSMutableArray類(lèi)對(duì)象的線(xiàn)程只能有一個(gè)
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

    dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSMutableArray *marray = [NSMutableArray new];
    for (int i = 0; i < 100000; i ++) {
        dispatch_async(global, ^{
            
            //等待dispatch_semaphore,
            //一直等待,知道dispatch_semaphore 技術(shù) >= 1
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            
            //由于dispatch_semaphore 技術(shù)>= 1,所以將dispatch_semaphore 計(jì)數(shù)值-1
            //dispatch_semaphore_wait 函數(shù)執(zhí)行返回,到此 計(jì)數(shù)為0
            //可以安全w訪(fǎng)問(wèn)
            [marray addObject:[NSNumber numberWithInteger:i]];
            
            //排他控制處理結(jié)束
            //所以通過(guò)dispatch_semaphore_signal 函數(shù) 將dispatch_semaphore 計(jì)數(shù) +1
            dispatch_semaphore_signal(semaphore);
            
        });
    }
    
  • 我們要下載很多圖片,并發(fā)異步進(jìn)行,每個(gè)下載都會(huì)開(kāi)辟一個(gè)新線(xiàn)程,可是我們又擔(dān)心太多線(xiàn)程肯定cpu吃不消,那么我們這里也可以用信號(hào)量控制一下最大開(kāi)辟線(xiàn)程數(shù)。

-(void)dispatchSignal{
    //crate的value表示,最多幾個(gè)資源可訪(fǎng)問(wèn)
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //任務(wù)1
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 1");
        sleep(1);
        NSLog(@"complete task 1");
        dispatch_semaphore_signal(semaphore);
    });
    //任務(wù)2
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 2");
        sleep(1);
        NSLog(@"complete task 2");
        dispatch_semaphore_signal(semaphore);
    });
    
    //任務(wù)3
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 3");
        sleep(1);
        NSLog(@"complete task 3");
        dispatch_semaphore_signal(semaphore);
    });
}

打印如下:

2019-01-15 18:52:27.165995+0800 GCDDemo01[3896:134773] run task 1
2019-01-15 18:52:27.166000+0800 GCDDemo01[3896:134771] run task 3
2019-01-15 18:52:28.171006+0800 GCDDemo01[3896:134773] complete task 1
2019-01-15 18:52:28.171014+0800 GCDDemo01[3896:134771] complete task 3
2019-01-15 18:52:28.171393+0800 GCDDemo01[3896:134774] run task 2
2019-01-15 18:52:29.176322+0800 GCDDemo01[3896:134774] complete task 2

總結(jié):由于設(shè)定的信號(hào)值為2,先執(zhí)行兩個(gè)線(xiàn)程,等執(zhí)行完一個(gè),才會(huì)繼續(xù)執(zhí)行下一個(gè),保證同一時(shí)間執(zhí)行的線(xiàn)程數(shù)不超過(guò)2。

  • 當(dāng) 創(chuàng)建信號(hào)量=1時(shí) dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

輸出:

2019-01-15 18:54:22.922276+0800 GCDDemo01[3935:136035] run task 1
2019-01-15 18:54:23.925043+0800 GCDDemo01[3935:136035] complete task 1
2019-01-15 18:54:23.925346+0800 GCDDemo01[3935:136038] run task 2
2019-01-15 18:54:24.927118+0800 GCDDemo01[3935:136038] complete task 2
2019-01-15 18:54:24.927407+0800 GCDDemo01[3935:136036] run task 3
2019-01-15 18:54:25.932808+0800 GCDDemo01[3935:136036] complete task 3

總結(jié):由于設(shè)定的信號(hào)值為1,先執(zhí)行1個(gè)線(xiàn)程,等執(zhí)行完一個(gè),才會(huì)繼續(xù)執(zhí)行下一個(gè),保證同一時(shí)間執(zhí)行的線(xiàn)程數(shù)不超過(guò)1

對(duì)比下面兩個(gè)方法的差異:

- (NSString *)sting
{
    __block NSString *s = nil;
    dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_async(globalDispatchQueue, ^{
        s = @"123";
    });
    
    return s;
}
- (NSString *)sting_dispatch_semaphore_t
{
    __block NSString *s = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_async(globalDispatchQueue, ^{
        
        s = @"123";
        dispatch_semaphore_signal(semaphore);

    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    return s;
}

技術(shù)信號(hào)的形式,等待異步執(zhí)行的結(jié)果,并將結(jié)果返回.

信號(hào)量:就是一種可用來(lái)控制訪(fǎng)問(wèn)資源的數(shù)量的標(biāo)識(shí),設(shè)定了一個(gè)信號(hào)量,在線(xiàn)程訪(fǎng)問(wèn)之前,加上信號(hào)量的處理,則可告知系統(tǒng)按照我們指定的信號(hào)量數(shù)量來(lái)執(zhí)行多個(gè)線(xiàn)程。

dispatch_once_t 一般創(chuàng)建單例
  • 保證應(yīng)用程序只能執(zhí)行一次制定處理的api
- (void)dispatchOnce
{
    // 保證程序只執(zhí)行一次,一般用于生成單例模式
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
    });
}
Dispatch I/O
  • 提高讀取速率,嘗試使用Dispatch I/O

GCD實(shí)現(xiàn)

  • Dispatch Queue
    通常,應(yīng)用程序中編寫(xiě)的線(xiàn)程管理用的代碼要在 系統(tǒng)級(jí)實(shí)現(xiàn)
  • Dispatch Source
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容