多線程 - GCD

內(nèi)容結(jié)構(gòu)框圖.png

1.GCD基本概念

GCD2個(gè)核心的概念

任務(wù):執(zhí)行什么操作
隊(duì)列:用來存放任務(wù)

GCD使用

(1)定制任務(wù)
(2)確定想做的事情

將任務(wù)添加到隊(duì)列中,GCD會自動的將隊(duì)列中的任務(wù)取出,放到對應(yīng)的線程中執(zhí)行
注意:任務(wù)的取出遵循先進(jìn)先出,后進(jìn)后出的原則。

GCD執(zhí)行任務(wù)函數(shù)

(1)同步方式執(zhí)行任務(wù) 
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
參數(shù)說明:queue: 隊(duì)列
        block: 任務(wù)
理解: 把右邊的任務(wù)(block)放到左邊的隊(duì)列(queue)中執(zhí)行

(2)異步方式執(zhí)行任務(wù)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

同步,異步的區(qū)別

同步:在當(dāng)前線程中執(zhí)行
異步:在另一條線程中執(zhí)行

串行,并發(fā)的區(qū)別

串行:一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)
并發(fā):多個(gè)任務(wù)并發(fā)同時(shí)執(zhí)行

1.獲取串行隊(duì)列
方式1:使用dispatch_queue_create創(chuàng)建串行隊(duì)列
dispatch_queue_t queue =  dispatch_queue_create("laowang", NULL);

方式2:使用主隊(duì)列 (和主線程相關(guān)的隊(duì)列)
主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列,放在主隊(duì)列中的任務(wù),都會放在主線程中執(zhí)行
dispatch_queue_t queue = dispatch_get_main_queue();

2.獲取并發(fā)隊(duì)列
方式1:使用dispatch_queue_create創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create(“l(fā)aowang”,
DISPATCH_QUEUE_CONCURRENT);

方式2:GCD默認(rèn)提供全局并發(fā)隊(duì)列,供整個(gè)應(yīng)用使用,不需要手動創(chuàng)建
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

各種隊(duì)列執(zhí)行效果

串行隊(duì)列為使用dispatch_queue_create 創(chuàng)建的串行隊(duì)列

異步并發(fā):
開啟新的線程,并發(fā)執(zhí)行

異步串行:
開啟新的線程,串行執(zhí)行

同步并發(fā):
不開啟新的線程,串行執(zhí)行任務(wù)

同步串行:
不開啟新的線程,串行執(zhí)行任務(wù)
隊(duì)列,任務(wù)總結(jié)
1.同步函數(shù)不具備開啟線程的能力,無論什么隊(duì)列都不會開始線程。
2.異步函數(shù)具備開啟線程的能力,開啟線程的條數(shù)由隊(duì)列決定。(串行隊(duì)列為1條,并行隊(duì)列為多條)

異步函數(shù)具備開線程的能力,但是不一定會開啟線程。

2.dispatch_group(調(diào)度組)

調(diào)度組最重要的目的,監(jiān)聽一組任務(wù)完成

如果想在dispatch_queue中所有的任務(wù)執(zhí)行完成后在做某種操作,在串行隊(duì)列中,可以把該操作放到最后一個(gè)任務(wù)執(zhí)行完成后繼續(xù),但是在并行隊(duì)列中怎么做呢。這就有dispatch_group 成組操作。

dispatch_group 使用函數(shù)

1.dispatch_group_async

dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue,dispatch_block_t block);
將任務(wù)(block)放入隊(duì)列queue,然后和調(diào)度組group關(guān)聯(lián)

dispatch_queue_t dispatchQueue = dispatch_queue_create("laowang", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dispatch-1");
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dspatch-2");
});
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
    NSLog(@"end");
});

上面的 log1 和log2輸出順序不定,因?yàn)槭窃诓l(fā)隊(duì)列上執(zhí)行,當(dāng)并發(fā)隊(duì)列全部執(zhí)行完成后,最后到main隊(duì)列上執(zhí)行一個(gè)操作,保證“end”是最后輸出。

2.dispatch_group_enter(group) dispatch_group_leave(group)

標(biāo)志著一個(gè)block(任務(wù))被加入了group。

dispatch_group_enter:增加當(dāng)前group執(zhí)行block數(shù)
dispatch_group_leave:減少當(dāng)前group執(zhí)行block數(shù)

調(diào)用dispatch_group_enter,dispatch_group_leave 可以非常適合處理異步任務(wù)同步(當(dāng)有多個(gè)異步請求時(shí),需要等待異步請求都結(jié)束時(shí)做些事情),當(dāng)異步任務(wù)開始前調(diào)用dispatch_group_enter,異步任務(wù)結(jié)束后調(diào)用dispatch_group_leave

例:
dispatch_group_t group = dispatch_group_create();

//1.任務(wù)開始調(diào)用enter
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    sleep(5);
    NSLog(@"任務(wù)一完成");
    
    //2.任務(wù)結(jié)束調(diào)用leave
    dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    NSLog(@"任務(wù)二完成");
    dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
    NSLog(@"任務(wù)完成");
});

注意

假如我們不想使用dispatch_group_async異步的將任務(wù)丟到group中去執(zhí)行,這時(shí)候就需要用到dispatch_group_enter跟dispatch_group_leave方法,這兩個(gè)方法要配對出現(xiàn),以下這兩種方法是等價(jià)的:

dispatch_group_async(group, queue, ^{ 

    任務(wù)
});

等價(jià)于:

dispatch_group_enter(group);

dispatch_async(queue, ^{

    //任務(wù)
    dispatch_group_leave(group);

});

3種情況:
1.調(diào)度組沒有任務(wù),直接執(zhí)行notify
2.調(diào)度組入組多余出組,notify永遠(yuǎn)不會執(zhí)行,因?yàn)榻M永遠(yuǎn)不會為空
3.出租多余入組,會奔潰

3.dispatch_group_notify

void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,
dispatch_block_t block);
當(dāng)group上所有的任務(wù)被執(zhí)行完畢以后,就會調(diào)用 dispatch_group_notify

4.dispatch_group_wait

dispatch_group_wait會同步地等待group中所有的block執(zhí)行完畢后才繼續(xù)執(zhí)行,類似于dispatch barrier

應(yīng)用場景

場景1:
  某個(gè)頁面加載時(shí)通過網(wǎng)絡(luò)請求獲得相應(yīng)的數(shù)據(jù),有的時(shí)候加載的內(nèi)容需要通過好幾個(gè)接口的數(shù)據(jù)組合而成,比如2個(gè)請求A和B,通常將B請求放在A請求成功回調(diào)中發(fā)起,在B的成功回調(diào)中組合起來。
  
  會產(chǎn)生的問題:
  1.請求多了,要寫多層的嵌套。
  2.如果在除了最后一個(gè)請求前的某個(gè)請求失敗了,不會執(zhí)行后面的請求。
  3.請求變成同步的,網(wǎng)絡(luò)差的情況下,如果有n個(gè)請求,以為著用戶要等待n倍于并發(fā)請求的時(shí)間才能看到內(nèi)容。
  
  假設(shè)要上傳4張圖片
NSMutableArray *imageURLs= [NSMutableArray array];

//1.創(chuàng)建dispatch_group任務(wù)組
dispatch_group_t group =dispatch_group_create();              

for (UIImage *image in images) {

    //2.往group里面增加一個(gè)block任務(wù),這邊的block任務(wù)就是上傳一張圖片
    dispatch_group_enter(group);                                  
sendPhoto(image, success:^(NSString *url) {

    [imageURLs addObject:url];
    
    //3.表示任務(wù)已經(jīng)完成 需要移除
    dispatch_group_leave(group);                           
});
}
    //4.當(dāng)group中的block任務(wù)都執(zhí)行完畢以后,dispatch_group_notify 會被執(zhí)行
    dispatch_group_notify(group, dispatch_get_global_queue(), ^{       
        
        postFeed(imageURLs, text);
});

GCD定時(shí)器

NStimer是對GCD定時(shí)器的包裝。GCD定時(shí)器更加的高級,且不受runloop的影響。

@interface ViewController ()

@property (nonatomic, strong) dispatch_source_t timer;

@end

int count = 0;
- (void)GCDTimer{
    
    1.獲得隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    2.創(chuàng)建一個(gè)定時(shí)器(dispatch_source_t本質(zhì)是一個(gè)OC對象)
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    3.設(shè)置定時(shí)器的各種屬性(幾時(shí)開始任務(wù),每個(gè)多長時(shí)間執(zhí)行一次)
    GCD的時(shí)間參數(shù),一般是納秒(1秒 == 10的9次方納秒)
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC);//何時(shí)開始第一個(gè)任務(wù)
    dispatch_time_t interval = 2.0 * NSEC_PER_SEC; //何時(shí)開始重復(fù)任務(wù)
    dispatch_source_set_timer(self.timer, start, interval, 0);
    
    4.設(shè)置回調(diào)
    dispatch_source_set_event_handler(self.timer, ^{
       
        NSLog(@"-------%@",[NSThread currentThread]);
        count ++;
        
        if(count == 5)
        {
            //取消定時(shí)器
            dispatch_cancel(self.timer);
            self.timer = nil;
        }
    });
    
    5.啟動timer
    dispatch_resume(self.timer);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 目錄:iOS多線程(一)--pthread、NSThreadiOS多線程(二)--GCD詳解iOS多線程(三)--...
    Claire_wu閱讀 1,136評論 0 6
  • 前言 GCD是蘋果為多核的并行運(yùn)算提出的解決方案,所以會自動合理地利用更多的CPU內(nèi)核(比如雙核、四核),最重要的...
    進(jìn)無盡閱讀 280評論 0 1
  • 目錄 一、基本概念1.多線程2.串行和并行, 并發(fā)3.隊(duì)列與任務(wù)4.同步與異步5.線程狀態(tài)6.多線程方案 二、GC...
    BohrIsLay閱讀 1,699評論 5 12
  • RunLoop的概念 一般來講,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完成后線程就會退出。如果我們需要一個(gè)機(jī)制,讓線程...
    SunZzzl閱讀 282評論 0 1

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