iOS多線程之GCD

Grand Central Dispatch (GCD)是Apple開(kāi)發(fā)的一個(gè)多核編程的解決方法。dispatch queue分成以下三種:

  • 運(yùn)行在主線程的Main_queue,通過(guò)dispatch_get_main_queue獲取。dispatch_get_main_queue也是一種dispatch_queue_t
  • 并行隊(duì)列global dispatch queue,通過(guò)dispatch_get_global_queue獲取,由系統(tǒng)創(chuàng)建三個(gè)不同優(yōu)先級(jí)的dispatch_queue。并行隊(duì)列的執(zhí)行順序與其加入隊(duì)列的順序相同。
  • 串行隊(duì)列serial queues一般用于按順序同步訪問(wèn),可創(chuàng)建任意數(shù)量的串行隊(duì)列,各個(gè)串行隊(duì)列之間是并發(fā)的。
    當(dāng)想要任務(wù)按照某一個(gè)特定的順序執(zhí)行時(shí),串行隊(duì)列是很有用的。串行隊(duì)列在同一個(gè)時(shí)間只執(zhí)行一個(gè)任務(wù)。我們可以使用串行隊(duì)列代替鎖去保護(hù)共享的數(shù)據(jù)。和鎖不同,一個(gè)串行隊(duì)列可以保證任務(wù)在一個(gè)可預(yù)知的順序下執(zhí)行。
    serial queues通過(guò)dispatch_queue_create創(chuàng)建,可以使用函數(shù)dispatch_retaindispatch_release去增加或者減少引用計(jì)數(shù)。
嚴(yán)格來(lái)說(shuō),GCD并非多線程,而是一個(gè)隊(duì)列。

GCD的用法:

1、并行隊(duì)列 + 同步執(zhí)行
- (void)gcdDemo1 {
    /* 
        DISPATCH_QUEUE_CONCURRENT 并發(fā)
        DISPATCH_QUEUE_SERIAL 串行
     */
    
    NSLog(@"嘿嘿-----------");
    dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(q, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            // 任務(wù)在并發(fā)子線程執(zhí)行 所以下面的"哈哈" 可能在for之前 也可能之后
            dispatch_async(q, ^{
                NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
            });
        }
    });
    
    NSLog(@"哈哈-----------");
}
2、 并行隊(duì)列 + 異步執(zhí)行
- (void)gcdDemo2 {
    /*
     先執(zhí)行"嘿嘿", 下面的代碼都是隨機(jī)執(zhí)行 "哈哈"可能在for前,后或者中間打印
     
     */
    NSLog(@"嘿嘿-----------");
    
    dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(q, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            dispatch_async(q, ^{
                NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
            });
        }
    });
    NSLog(@"哈哈-----------");
}
3、串行隊(duì)列 + 同步執(zhí)行
- (void)gcdDemo3 {
    
    /*
     DISPATCH_QUEUE_SERIAL : 串行
     按照順序執(zhí)行
     */
    NSLog(@"嘿嘿-----------");
    dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(q, ^{
        for (NSInteger i = 0; i < 10; i ++) {
            dispatch_async(q, ^{
                NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
            });
        }
    });
    NSLog(@"哈哈-----------");
}
4、串行隊(duì)列 + 異步執(zhí)行

- (void)gcdDemo4 {
    
    /*
        先執(zhí)行"嘿嘿", 后面的會(huì)隨機(jī)執(zhí)行, "哈哈"可能會(huì)插入到for中打印
        for是在串行異步執(zhí)行 所以子線程會(huì)執(zhí)行完當(dāng)前任務(wù)后才會(huì)執(zhí)行下次任務(wù)
     */
    
    NSLog(@"嘿嘿-----------");
    dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_SERIAL);
    dispatch_async(q, ^{
        for (NSInteger i = 0; i < 2; i ++) {
            dispatch_async(q, ^{
                NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
            });
        }
    });
    NSLog(@"哈哈-----------");
    
}
5、主隊(duì)列 + 同步執(zhí)行
- (void)gcdDemo5 {
    
    /*
        "嘿嘿"等待for的任務(wù)執(zhí)行完往下執(zhí)行 而for又等待"嘿嘿"執(zhí)行完往下執(zhí)行 所以互相等待 程序卡死 崩潰
     */
    NSLog(@"嘿嘿-----------");
    dispatch_sync(dispatch_get_main_queue(), ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
        }
    });
    
    NSLog(@"哈哈-----------");
}
6、主隊(duì)列 + 異步執(zhí)行
- (void)gcdDemo6 {
    /*
        先執(zhí)行"嘿嘿"->"哈哈"->for里面 for里面的任務(wù)會(huì)在主隊(duì)列執(zhí)行 添加到主隊(duì)列后不會(huì)立馬就執(zhí)行的 會(huì)等待一會(huì) 所以"哈哈"在for之前就執(zhí)行了 因?yàn)樗旧砭驮谥骶€程中無(wú)需等待
     */
    NSLog(@"嘿嘿-----------");
    dispatch_async(dispatch_get_main_queue(), ^{
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
        }
    });
    
    NSLog(@"哈哈-----------");
}
7、柵欄方法
  • dispatch_barrier_async
    有時(shí)需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后,才能開(kāi)始執(zhí)行第二組操作。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來(lái),操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到dispatch_barrier_async方法在兩個(gè)操作組間形成柵欄。
- (void)gcdDemo7 {

    /*
        "1"和"2"處于子線程會(huì)隨機(jī)打印順序 然后打印柵欄"3" 最后打印"4"
     */
    
   dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(q, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    
    dispatch_async(q, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });
    
    dispatch_barrier_async(q, ^{
        for (NSInteger i = 0; i < 10; i ++) {
//            // 加入到異步后 下面的"4"可能在for之前打印, 可能在中間打印, 也可能在for后面打印
//            dispatch_async(q, ^{
//                NSLog(@"----3 barrier-----%@", [NSThread currentThread]);
//            });
            
            NSLog(@"----3 barrier-----%@", [NSThread currentThread]);
        }
    });
    
    dispatch_async(q, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    }); 
}
8、快速迭代方法 dispatch_apply

dispatch_apply函數(shù)是dispatch_sync函數(shù)和Dispatch_Group的關(guān)聯(lián)API,該函數(shù)按指定的次數(shù)將指定的Block追加到指定的Dispatch_Queue中,并等到全部的處理執(zhí)行結(jié)束。

- (void)gcdDemo8 {

    dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    /**
     同時(shí)遍歷6次 不按順序輸出
     @param iterations 遍歷的次數(shù)
     @param queue 在哪個(gè)隊(duì)列
     @param size_t index下標(biāo)號(hào)
     */
    dispatch_apply(6, q, ^(size_t index) {
        NSLog(@"%zd----%@", index, [NSThread currentThread]);
    });
}
9、隊(duì)列組 dispatch_group

多個(gè)任務(wù)處理完畢之后想執(zhí)行結(jié)束處理,這種需求會(huì)經(jīng)常出現(xiàn)。如果只是使用一個(gè)Serial Dispatch Queue(串行隊(duì)列)時(shí),只要將想執(zhí)行的處理全部追加到該串行隊(duì)列中并在最后追加結(jié)束處理即可,但是在使用Concurrent Queue 時(shí),可能會(huì)同時(shí)使用多個(gè)Dispatch Queue時(shí),代碼就會(huì)變得很復(fù)雜。在這種情況下,就可以使用Dispatch Group。

- (void)gcdDemo9 {
    /*
        "3"會(huì)等待前面的"1""2"打印完才會(huì)打印, "1""2"異步操作會(huì)交替打印
     */
    // 創(chuàng)建一個(gè)組隊(duì)列
    dispatch_group_t group = dispatch_group_create();
    // 創(chuàng)建一個(gè)異步組隊(duì)列
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 執(zhí)行1個(gè)耗時(shí)的異步操作
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"1----%zd---%@",i, [NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 執(zhí)行1個(gè)耗時(shí)的異步操作
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"2----%zd---%@",i, [NSThread currentThread]);
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的異步操作都執(zhí)行完畢后,回到主線程...
        for (NSInteger i = 0; i < 10; i ++) {
            NSLog(@"3----%zd---%@",i, [NSThread currentThread]);
        }
    });
}
10、信號(hào)量dispatch_semaphore

項(xiàng)目中的業(yè)務(wù)接口請(qǐng)求的時(shí)候需要Token驗(yàn)證。我們最簡(jiǎn)化這個(gè)需求就是:兩個(gè)請(qǐng)求,請(qǐng)求1成功返回所需參數(shù)之后,才能開(kāi)始請(qǐng)求2。

// 信號(hào)量
- (void)dispatch_semaphore {
    
    /*
     //創(chuàng)建信號(hào)量
     dispatch_semaphore_create
     //發(fā)送信號(hào)量
     dispatch_semaphore_signal
     //等待信號(hào)量
     dispatch_semaphore_wait
     */
    
    dispatch_async(dispatch_queue_create("ptl", DISPATCH_QUEUE_CONCURRENT), ^{
        
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        // 請(qǐng)求接口1
        [self getToken:semaphore];
        //此時(shí)的信號(hào)量為0,只有token請(qǐng)求成功發(fā)送信號(hào)量之后,才會(huì)往下執(zhí)行[self request]方法,否則會(huì)一直等下去;
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        // 請(qǐng)求接口2
        [self requestData1:semaphore];
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        // 請(qǐng)求接口3
        [self requestData2];
    });
}

- (void)getToken:(dispatch_semaphore_t)semaphore
{
    // 模仿網(wǎng)絡(luò)請(qǐng)求
    for (NSInteger i = 0; i < 20; i ++) {
        NSLog(@"請(qǐng)求1-------%zd", i);
        // 模仿請(qǐng)求成功
        //成功拿到token,發(fā)送信號(hào)量
        dispatch_semaphore_signal(semaphore);
    }
    NSLog(@"------------請(qǐng)求1成功-----------------");
}

- (void)requestData1:(dispatch_semaphore_t)semaphore {
    for (NSInteger i = 0; i < 20; i ++) {
        NSLog(@"請(qǐng)求2-------%zd", i);
        dispatch_semaphore_signal(semaphore);
    }
    NSLog(@"------------請(qǐng)求2成功-----------------");
}

- (void)requestData2 {
    for (NSInteger i = 0; i < 20; i ++) {
        NSLog(@"請(qǐng)求3-------%zd", i);
    }
    NSLog(@"------------請(qǐng)求3成功-----------------");
}

Github: https://github.com/soliloquy-local/GCD.git
以上就是對(duì)GCD各個(gè)情況的簡(jiǎn)單介紹,主要以代碼為主...??

最后編輯于
?著作權(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)容

  • 1. GCD簡(jiǎn)介 什么是GCD呢?我們先來(lái)看看百度百科的解釋簡(jiǎn)單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 522評(píng)論 0 0
  • GCD (Grand Central Dispatch) :iOS4 開(kāi)始引入,使用更加方便,程序員只需要將任務(wù)添...
    池鵬程閱讀 1,443評(píng)論 0 2
  • 多線程 在iOS開(kāi)發(fā)中為提高程序的運(yùn)行效率會(huì)將比較耗時(shí)的操作放在子線程中執(zhí)行,iOS系統(tǒng)進(jìn)程默認(rèn)啟動(dòng)一個(gè)主線程,用...
    郭豪豪閱讀 2,726評(píng)論 0 4
  • iOS多線程之GCD 什么是GCD GCD(grand central dispatch) 是 libdispat...
    comst閱讀 1,343評(píng)論 0 0
  • 解決方法: webpack.dev.conf.js文件下找到devServer,將其中host設(shè)置為 host: ...
    凌奕閱讀 4,624評(píng)論 0 0

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