iOS多線程之GCD

1. GCD相關(guān)概念

任務(wù):就是執(zhí)行操作的意思玄呛,就是你在線程中執(zhí)行的那段代碼蛉顽。在 GCD 中是放在 block 中的尤筐。執(zhí)行任務(wù)有兩種方式:『同步執(zhí)行』『異步執(zhí)行』诉探。兩者的主要區(qū)別是:是否等待隊(duì)列的任務(wù)執(zhí)行結(jié)束日熬,以及是否具備開啟新線程的能力。

同步執(zhí)行(sync):同步添加任務(wù)到指定的隊(duì)列中肾胯,在添加的任務(wù)執(zhí)行結(jié)束之前竖席,會(huì)一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行敬肚。只能在當(dāng)前線程中執(zhí)行任務(wù)毕荐,不具備開啟新線程的能力。

異步執(zhí)行(async):異步添加任務(wù)到指定的隊(duì)列中艳馒,它不會(huì)做任何等待憎亚,可以繼續(xù)執(zhí)行任務(wù)员寇。可以在新的線程中執(zhí)行任務(wù)第美,具備開啟新線程的能力蝶锋。

隊(duì)列(Dispatch Queue):這里的隊(duì)列指執(zhí)行任務(wù)的等待隊(duì)列,即用來存放任務(wù)的隊(duì)列什往。隊(duì)列是一種特殊的線性表扳缕,采用 FIFO(先進(jìn)先出)的原則,即新任務(wù)總是被插入到隊(duì)列的末尾别威,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取躯舔。每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)兔港。

串行隊(duì)列(Serial Dispatch Queue):每次只有一個(gè)任務(wù)被執(zhí)行庸毫。讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行。(只開啟一個(gè)線程衫樊,一個(gè)任務(wù)執(zhí)行完畢后飒赃,再執(zhí)行下一個(gè)任務(wù))

并發(fā)隊(duì)列(Concurrent Dispatch Queue):可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行。(可以開啟多個(gè)線程科侈,并且同時(shí)執(zhí)行任務(wù))。并發(fā)隊(duì)列 的并發(fā)功能只有在異步(dispatch_async)方法下才有效臀栈。

2. GCD 的使用步驟

1.創(chuàng)建一個(gè)隊(duì)列(串行隊(duì)列或并發(fā)隊(duì)列)蔫慧;
2.將任務(wù)追加到任務(wù)的等待隊(duì)列中,然后系統(tǒng)就會(huì)根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或異步執(zhí)行)权薯。

3. 隊(duì)列的創(chuàng)建方法 / 獲取方法

使用 dispatch_queue_create 方法來創(chuàng)建隊(duì)列姑躲。該方法需要傳入兩個(gè)參數(shù),第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識(shí)符,用于 DEBUG盟蚣,可為空黍析。第二個(gè)參數(shù)用來識(shí)別是串行隊(duì)列還是并發(fā)隊(duì)列。DISPATCH_QUEUE_SERIAL 表示串行隊(duì)列屎开,DISPATCH_QUEUE_CONCURRENT 表示并發(fā)隊(duì)列阐枣。
// 串行隊(duì)列的創(chuàng)建方法
dispatch_queue_t  queue = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊(duì)列的創(chuàng)建方法
dispatch_queue_t  queue = dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);

對于串行隊(duì)列,GCD 提供了的一種特殊的串行隊(duì)列:『主隊(duì)列(Main Dispatch Queue)』奄抽。所有放在主隊(duì)列中的任務(wù)蔼两,都會(huì)放到主線程中執(zhí)行〕讯龋可使用 dispatch_get_main_queue() 方法獲得主隊(duì)列额划。
// 主隊(duì)列的獲取方法
dispatch_queue_t queue = dispatch_get_main_queue();

對于并發(fā)隊(duì)列,GCD 默認(rèn)提供了 『全局并發(fā)隊(duì)列(Global Dispatch Queue)』档泽。
可以使用 dispatch_get_global_queue 方法來獲取全局并發(fā)隊(duì)列锁孟。需要傳入兩個(gè)參數(shù)彬祖。第一個(gè)參數(shù)表示隊(duì)列優(yōu)先級,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT品抽。第二個(gè)參數(shù)暫時(shí)沒用,用 0 即可甜熔。
// 全局并發(fā)隊(duì)列的獲取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

4. 任務(wù)的創(chuàng)建方法

// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
    
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
    
});

5.1 隊(duì)列和任務(wù)的組合使用

區(qū)別 并發(fā)隊(duì)列 串行隊(duì)列 主隊(duì)列
同步(sync) 沒有開啟新線程圆恤,串行執(zhí)行任務(wù) 沒有開啟新線程,串行執(zhí)行任務(wù) 死鎖腔稀,卡住不執(zhí)行
異步(async) 有開啟新線程盆昙,并發(fā)執(zhí)行任務(wù) 有開啟新線程(1條),串行執(zhí)行任務(wù) 沒有開啟新線程焊虏,串行執(zhí)行任務(wù)
從上邊可看出:『主線程』 中調(diào)用 『主隊(duì)列』+『同步執(zhí)行』 會(huì)導(dǎo)致死鎖問題淡喜。
這是因?yàn)?主隊(duì)列中追加的同步任務(wù) 和 主線程本身的任務(wù) 兩者之間相互等待,阻塞了 『主隊(duì)列』诵闭,最終造成了主隊(duì)列所在的線程(主線程)死鎖問題炼团。
而如果我們在 『其他線程』 調(diào)用 『主隊(duì)列』+『同步執(zhí)行』,則不會(huì)阻塞 『主隊(duì)列』疏尿,自然也不會(huì)造成死鎖問題瘟芝。最終的結(jié)果是:不會(huì)開啟新線程,串行執(zhí)行任務(wù)褥琐。

5.2 隊(duì)列嵌套情況下锌俱,不同組合方式區(qū)別

區(qū)別 『異步執(zhí)行+并發(fā)隊(duì)列』嵌套『同一個(gè)并發(fā)隊(duì)列』 『同步執(zhí)行+并發(fā)隊(duì)列』嵌套『同一個(gè)并發(fā)隊(duì)列』 『異步執(zhí)行+串行隊(duì)列』嵌套『同一個(gè)串行隊(duì)列』 『同步執(zhí)行+串行隊(duì)列』嵌套『同一個(gè)串行隊(duì)列』
同步(sync) 沒有開啟新的線程,串行執(zhí)行任務(wù) 沒有開啟新線程敌呈,串行執(zhí)行任務(wù) 死鎖卡住不執(zhí)行 死鎖卡住不執(zhí)行
異步(async) 有開啟新線程贸宏,并發(fā)執(zhí)行任務(wù) 有開啟新線程,并發(fā)執(zhí)行任務(wù) 有開啟新線程(1 條)磕洪,串行執(zhí)行任務(wù) 有開啟新線程(1 條)吭练,串行執(zhí)行任務(wù)

6. 具體代碼組合使用詳情 ( 線程number=1 就是主線程 大于1就是子線程 )

  1. 同步(sync) + 并發(fā)隊(duì)列 ( 沒有開啟子線程,串行執(zhí)行任務(wù),在主線程 )

//  同步(sync) + 并發(fā)隊(duì)列 (沒有開啟子線程,串行執(zhí)行任務(wù),在主線程)

-(void)startGCDAction{
    
    // 1.1 創(chuàng)建并發(fā)隊(duì)列
 // dispatch_queue_t  queueObj = dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
    
    // 1.2 獲取全局并發(fā)隊(duì)列
    dispatch_queue_t  queueObj = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2.0 同步
    dispatch_sync(queueObj, ^{
        NSLog(@"做任務(wù)(1),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queueObj, ^{
        NSLog(@"做任務(wù)(2),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queueObj, ^{
        NSLog(@"做任務(wù)(3),當(dāng)前線程:%@",[NSThread currentThread]);
    });
}

最終運(yùn)行結(jié)果:

TestModel[85321:13209706] 做任務(wù)(1),當(dāng)前線程:<NSThread: 0x281beda40>{number = 1, name = main}
TestModel[85321:13209706] 做任務(wù)(2),當(dāng)前線程:<NSThread: 0x281beda40>{number = 1, name = main}
TestModel[85321:13209706] 做任務(wù)(3),當(dāng)前線程:<NSThread: 0x281beda40>{number = 1, name = main}
 
  1. 異步(async) + 并發(fā)隊(duì)列 ( 有開啟子線程,并發(fā)執(zhí)行任務(wù),在子線程 )

// 異步(async) + 并發(fā)隊(duì)列(有開啟子線程,并發(fā)執(zhí)行任務(wù),在子線程)

-(void)startGCDAction{
    
     // 1.1 創(chuàng)建并發(fā)隊(duì)列
     dispatch_queue_t  queueObj = dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
    
     // 1.2 獲取全局并發(fā)隊(duì)列
//    dispatch_queue_t  queueObj = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.0 異步
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(1),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(2),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(3),當(dāng)前線程:%@",[NSThread currentThread]);
    });
}

最終運(yùn)行結(jié)果:

TestModel[85332:13211973] 做任務(wù)(2),當(dāng)前線程:<NSThread: 0x281405a80>{number = 4, name = (null)}
TestModel[85332:13211979] 做任務(wù)(1),當(dāng)前線程:<NSThread: 0x281408cc0>{number = 3, name = (null)}
TestModel[85332:13211973] 做任務(wù)(3),當(dāng)前線程:<NSThread: 0x281405a80>{number = 4, name = (null)}
  1. 同步(sync) + 串行隊(duì)列 ( 沒有開啟子線程,串行執(zhí)行任務(wù),在主線程 )

// 同步(sync) + 串行隊(duì)列 (沒有開啟子線程,串行執(zhí)行任務(wù),在主線程)

-(void)startGCDAction{
    
    // 1.0 創(chuàng)建串行隊(duì)列
    dispatch_queue_t  queueObj = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
    
    // 2.0 同步
    dispatch_sync(queueObj, ^{
        NSLog(@"做任務(wù)(1),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queueObj, ^{
        NSLog(@"做任務(wù)(2),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queueObj, ^{
        NSLog(@"做任務(wù)(3),當(dāng)前線程:%@",[NSThread currentThread]);
    });
}    

最終運(yùn)行結(jié)果:

TestModel[85392:13219612] 做任務(wù)(1),當(dāng)前線程:<NSThread: 0x2822a5a40>{number = 1, name = main}
TestModel[85392:13219612] 做任務(wù)(2),當(dāng)前線程:<NSThread: 0x2822a5a40>{number = 1, name = main}
TestModel[85392:13219612] 做任務(wù)(3),當(dāng)前線程:<NSThread: 0x2822a5a40>{number = 1, name = main}
  1. 異步(async) + 串行隊(duì)列 ( 只開啟一條子線程,串行執(zhí)行任務(wù) )

// 異步(async) + 串行隊(duì)列 (只開啟一條子線程,串行執(zhí)行任務(wù))

-(void)startGCDAction{
    
    // 1.0 創(chuàng)建串行隊(duì)列
    dispatch_queue_t  queueObj = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
    
    // 2.0 異步
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(1),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(2),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(3),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    
}

最終運(yùn)行結(jié)果:

TestModel[85396:13221073] 做任務(wù)(1),當(dāng)前線程:<NSThread: 0x28106c940>{number = 3, name = (null)}
TestModel[85396:13221073] 做任務(wù)(2),當(dāng)前線程:<NSThread: 0x28106c940>{number = 3, name = (null)}
TestModel[85396:13221073] 做任務(wù)(3),當(dāng)前線程:<NSThread: 0x28106c940>{number = 3, name = (null)}

  1. 同步(sync) + 主線程 ( 造成死鎖,主線程和同步任務(wù)相互等待 )
   
   // 同步(sync) + 主線程 (造成死鎖,主線程和同步任務(wù)相互等待)
   
   -(void)startGCDActionWithMainThread{
       
       // 1.0 獲取主線程
       dispatch_queue_main_t  mainQueue = dispatch_get_main_queue();
       
       // 2.0 同步
       dispatch_sync(mainQueue, ^{
           NSLog(@"做任務(wù)(1),當(dāng)前線程:%@",[NSThread currentThread]);
       });
       
       dispatch_sync(mainQueue, ^{
           NSLog(@"做任務(wù)(2),當(dāng)前線程:%@",[NSThread currentThread]);
       });
       
       NSLog(@"做任務(wù)(3),當(dāng)前線程:%@",[NSThread currentThread]);
       
   }

   最終運(yùn)行結(jié)果:
   
   Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1004cb84c)
     
   在 dispatch_sync(mainQueue, ^{ }); 報(bào)上訴錯(cuò)誤 三個(gè)都不會(huì)打印 因?yàn)槌绦蛑苯颖紳?
  1. 異步(async) + 主線程 ( 沒有開啟子線程,串行執(zhí)行任務(wù),在主線程 )

// 異步(async) + 主線程 (沒有開啟子線程,串行執(zhí)行任務(wù),在主線程)

-(void)startGCDActionWithMainThread{
    
    // 1.0 獲取主線程
    dispatch_queue_main_t  mainQueue = dispatch_get_main_queue();
    
    // 2.0 異步
    dispatch_async(mainQueue, ^{
        NSLog(@"做任務(wù)(1),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(mainQueue, ^{
        NSLog(@"做任務(wù)(2),當(dāng)前線程:%@",[NSThread currentThread]);
    });
}

最終運(yùn)行結(jié)果:

TestModel[85332:13211973] 做任務(wù)(1),當(dāng)前線程:<NSThread: 0x280406e40>{number = 1, name = main}
TestModel[85332:13211973] 做任務(wù)(2),當(dāng)前線程:<NSThread: 0x280406e40>{number = 1, name = main}

7. GCD 線程之間的通信

在其子線程中完成了耗時(shí)操作后,需要回到主線程 (進(jìn)行 UI 刷新) 褐鸥,那么就用到了線程之間的通訊线脚。通過使用

1.1 獲取主線程 dispatch_queue_t mainQueue = dispatch_get_main_queue();

1.2 異步回到主線程 dispatch_async(mainQueue, ^{ } );

// 線程之間的通訊(子線程回到主線程)
-(void)startGCDCommunicationAction{
    
    // 1.1 創(chuàng)建并發(fā)隊(duì)列
    // dispatch_queue_t queueObj=dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
    
    // 1.2 獲取全局并發(fā)隊(duì)列
    dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.0 異步
    dispatch_async(queueObj, ^{
        
        NSLog(@"做任務(wù)(1),當(dāng)前線程:%@",[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:3.0];  // 睡眠線程3秒,相當(dāng)于耗時(shí)操作
        
        // 3.0 獲取主線程
        dispatch_queue_t mainQueue=dispatch_get_main_queue();
        
        // 4.0 異步回到主線程
        dispatch_async(mainQueue, ^{
            
            NSLog(@"回到主線程刷新UI操作,當(dāng)前線程:%@",[NSThread currentThread]);
            
        });
        
    });
   
}

最終運(yùn)行結(jié)果:

2019-08-11 12:26:51.973353 TestModel[85877:13291589] 做任務(wù)(1),當(dāng)前線程:<NSThread: 0x282f797c0>{number = 4, name = (null)}
2019-08-11 12:26:54.979122 TestModel[85877:13291523] 回到主線程刷新UI操作,當(dāng)前線程:<NSThread: 0x282f1ae40>{number = 1, name = main}

8. GCD 的其他方法

1. 柵欄函數(shù)(方法):dispatch_barrier_sync (同步 ) 和 dispatch_barrier_async (異步 ) 執(zhí)行完柵欄前面的操作之后,才執(zhí)行柵欄操作(子線程或主線程)叫榕,最后再執(zhí)行柵欄后邊的操作浑侥。

// 使用柵欄函數(shù),必須通過創(chuàng)建并發(fā)隊(duì)列 不能使用全局并發(fā)隊(duì)列
-(void)startGCDWithBarrier{
    
    // 1.0 創(chuàng)建并發(fā)隊(duì)列 (這里不能用下面的 直接獲取全局并發(fā)隊(duì)列 只能通過創(chuàng)建才能體現(xiàn)出柵欄函數(shù))
    dispatch_queue_t queueObj=dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);
    
    // 1.0 獲取全局并發(fā)隊(duì)列
//    dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.0 異步操作1
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(1),當(dāng)前線程:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];  // 睡眠線程1秒
    });
    // 2.0 異步操作2
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(2),當(dāng)前線程:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];  // 睡眠線程2秒
    });
    
    // 3.0 同步柵欄函數(shù)(在主線程執(zhí)行操作,換成 async 就在子線程中)
    dispatch_barrier_sync(queueObj, ^{
        NSLog(@"柵欄函數(shù)做任務(wù)(????),當(dāng)前線程:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];  // 睡眠線程2秒
    });
    
    // 2.0異步操作3
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(3),當(dāng)前線程:%@",[NSThread currentThread]);
    });
    // 2.0異步操作4
    dispatch_async(queueObj, ^{
        NSLog(@"做任務(wù)(4),當(dāng)前線程:%@",[NSThread currentThread]);
    });
   
}

最終運(yùn)行結(jié)果:

2019-08-11 12:49:37.631269 TestModel[85906:13296017] 做任務(wù)(1),當(dāng)前線程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
2019-08-11 12:49:38.632798 TestModel[85906:13296017] 做任務(wù)(2),當(dāng)前線程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
2019-08-11 12:49:40.634186 TestModel[85906:13295953] 柵欄函數(shù)做任務(wù)(????),當(dāng)前線程:<NSThread: 0x2806d3040>{number = 1, name = main}
2019-08-11 12:49:41.635503 TestModel[85906:13296017] 做任務(wù)(3),當(dāng)前線程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
2019-08-11 12:49:41.635610 TestModel[85906:13296017] 做任務(wù)(4),當(dāng)前線程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}

2. 延時(shí)執(zhí)行函數(shù)(方法):dispatch_after 其并不是在指定時(shí)間之后才開始執(zhí)行處理,而是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中(一定在主線程)晰绎。

// GCD延時(shí)函數(shù)
-(void)startGCDWithAfterFun{
    
    NSLog(@"來咯寓落!來咯!她們都來了 ?? ");
    
    // 獲取全局的并發(fā)隊(duì)列
    dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queueObj, ^{
        
        NSLog(@"調(diào)用前的線程是:%@ ",[NSThread currentThread]);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
            NSLog(@"3秒后才調(diào)用我這個(gè)操作,當(dāng)前線程是:%@ ",[NSThread currentThread]);
            
        });
        
    });
}

最終運(yùn)行結(jié)果:

2019-08-11 13:09:48.371427 TestModel[85920:13298751] 來咯荞下!來咯伶选!她們都來了 ?? 
2019-08-11 13:09:48.371942 TestModel[85920:13298827] 調(diào)用前的線程是:<NSThread: 0x280b38f80>{number = 4, name = (null)} 
2019-08-11 13:09:51.613677 TestModel[85920:13298751] 3秒后才調(diào)用我這個(gè)操作,當(dāng)前線程是:<NSThread: 0x280b55880>{number = 1, name = main} 

3. 一次性函數(shù)(方法):dispatch_once 該函數(shù)中的代碼整個(gè)程序運(yùn)行過程中只會(huì)執(zhí)行一次史飞。

// GCD一次性函數(shù)(只會(huì)執(zhí)行一次)
-(void)startGCDWithOnceFun{
    
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        
        NSLog(@"你只能打我一次哦(??????),不服來戰(zhàn)!");
    });
    
}


我在 -(void)viewDidLoad {} 中調(diào)用多次,最終結(jié)果只會(huì)輸出一次

-(void)viewDidLoad {
    [super viewDidLoad];
  
    [self startGCDWithOnceFun];
    [self startGCDWithOnceFun];
    [self startGCDWithOnceFun];
    [self startGCDWithOnceFun];
}

最終運(yùn)行結(jié)果:

TestModel[85923:13300176] 你只能打我一次哦(??????),不服來戰(zhàn)仰税!

4. GCD 隊(duì)列組 dispatch_group_async 分別異步執(zhí)行2個(gè)耗時(shí)任務(wù)构资,然后當(dāng)2個(gè)耗時(shí)任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)。監(jiān)聽隊(duì)列組中任務(wù)完成 dispatch_group_notify

// GCD之隊(duì)列組
-(void)startGCDWithGroup{
    
    
    // 1.0 創(chuàng)建一個(gè)隊(duì)列組
    dispatch_group_t groupObj=dispatch_group_create();
    
    // 2.1 創(chuàng)建并發(fā)隊(duì)列
    dispatch_queue_t queueObj=dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);
    
    // 2.2 獲取全局的并發(fā)隊(duì)列
//    dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
   // 3.1 使用隊(duì)列組執(zhí)行任務(wù)(1)
    dispatch_group_async(groupObj, queueObj, ^{
        NSLog(@"列組中,子線程執(zhí)行任務(wù)(1),當(dāng)前線程是:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];
        
    });
    
    // 3.1 使用隊(duì)列組執(zhí)行任務(wù)(1)
    dispatch_group_async(groupObj, queueObj, ^{
        NSLog(@"列組中,子線程隊(duì)執(zhí)行任務(wù)(2),當(dāng)前線程是:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3.0];
        
    });
    
    // 4.0 隊(duì)列組中任務(wù)執(zhí)行完畢 回到主線程
    dispatch_group_notify(groupObj, dispatch_get_main_queue(), ^{
        NSLog(@"隊(duì)列組中任務(wù)執(zhí)行完畢調(diào)用,回到主線程,當(dāng)前線程是:%@",[NSThread currentThread]);
    });
  
}

最終運(yùn)行結(jié)果:

2019-08-11 17:07:44.674 TestModel[86321:13347506] 列組中,子線程隊(duì)執(zhí)行任務(wù)(2),當(dāng)前線程是:<NSThread: 0x281365880>{number = 3, name = (null)}
2019-08-11 17:07:44.675 TestModel[86321:13347505] 列組中,子線程執(zhí)行任務(wù)(1),當(dāng)前線程是:<NSThread: 0x281330940>{number = 4, name = (null)}
2019-08-11 17:07:47.680 TestModel[86321:13347495] 隊(duì)列組中任務(wù)執(zhí)行完畢調(diào)用,回到主線程,當(dāng)前線程是:<NSThread: 0x281351340>{number = 1, name = main}

5. dispatch_group_wait : 暫停當(dāng)前線程(阻塞當(dāng)前線程) dispatch_semaphore :(線程加鎖)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陨簇,一起剝皮案震驚了整個(gè)濱河市吐绵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌河绽,老刑警劉巖己单,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耙饰,居然都是意外死亡纹笼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門苟跪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來廷痘,“玉大人,你說我怎么就攤上這事削咆‰故瑁” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵拨齐,是天一觀的道長鳞陨。 經(jīng)常有香客問我,道長瞻惋,這世上最難降的妖魔是什么厦滤? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮歼狼,結(jié)果婚禮上掏导,老公的妹妹穿的比我還像新娘。我一直安慰自己趟咆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布梅屉。 她就那樣靜靜地躺著值纱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坯汤。 梳的紋絲不亂的頭發(fā)上虐唠,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音惰聂,去河邊找鬼疆偿。 笑死咱筛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杆故。 我是一名探鬼主播迅箩,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼反番!你這毒婦竟也來了沙热?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤罢缸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后投队,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枫疆,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年敷鸦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了息楔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扒披,死狀恐怖值依,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碟案,我是刑警寧澤愿险,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站价说,受9級特大地震影響辆亏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鳖目,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一扮叨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧领迈,春花似錦彻磁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至薪贫,卻和暖如春恍箭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瞧省。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工扯夭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鳍贾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓交洗,卻偏偏與公主長得像骑科,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子构拳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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