iOS多線程(二)

一、GCD簡(jiǎn)介

  • 什么是GCD?

    全稱是 Grand Central Dispatch

    純 C 語(yǔ)言,提供了非常多強(qiáng)大的函數(shù)

  • GCD的優(yōu)勢(shì)

    GCD 是蘋果公司為多核的并行運(yùn)算提出的解決方案

    GCD 會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)

    GCD 會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)

    程序員只需要告訴GCD 想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼

  • 劣勢(shì)

    自動(dòng)控制生命周期,程序員不能自由操作

  • 函數(shù)

    - (void)syncTest
    {
        // 把任務(wù)添加到隊(duì)列 --> 函數(shù)
        // 任務(wù)   ( _t ref 都是 c對(duì)象)
        dispatch_block_t block = ^{
            NSLog(@"hello GCD");
        };
        //串行隊(duì)列 <- 因?yàn)榈诙€(gè)參數(shù)是NULL
        dispatch_queue_t queue = dispatch_queue_create("com.hehe.cn", NULL);
        // 函數(shù)
        dispatch_async(queue, block);
    }
    
    • 任務(wù)使用 block 封裝
    • 任務(wù)的 block 沒(méi)有參數(shù)也沒(méi)有返回值
    • 執(zhí)行任務(wù)的函數(shù)
      • 異步 dispatch_async
        • 不用等待當(dāng)前語(yǔ)句執(zhí)行完畢,就可以執(zhí)行下一條語(yǔ)句
        • 會(huì)開啟線程執(zhí)行 block的任務(wù)
        • 異步是多線程的代名詞
      • 同步 dispatch_sync
        • 必須等待當(dāng)前語(yǔ)句執(zhí)行完畢,才會(huì)執(zhí)行下一條語(yǔ)句
        • 不會(huì)開啟線程
        • 在當(dāng)前執(zhí)行 block的任務(wù)
  • 隊(duì)列

    串行隊(duì)列 - DISPATCH_QUEUE_SERIAL: 只能一個(gè)一個(gè)任務(wù)挨個(gè)出去

    并行隊(duì)列 - DISPATCH_QUEUE_CONCURRENT: 多個(gè)任務(wù)同時(shí)出去

  • 隊(duì)列和函數(shù)的組合

    • 同步函數(shù)串行隊(duì)列:

      1、不會(huì)開啟線程,在當(dāng)前線程執(zhí)行任務(wù)

      2、任務(wù)串行執(zhí)行,任務(wù)一個(gè)接著一個(gè)

      3、會(huì)產(chǎn)生堵塞

    • 同步函數(shù)并發(fā)隊(duì)列:

      1、不會(huì)開啟線程,在當(dāng)前線程執(zhí)行任務(wù)

      2、任務(wù)一個(gè)接著一個(gè)

    • 異步函數(shù)串行隊(duì)列:

      1、開啟線程一條新線程

      2、任務(wù)一個(gè)接著一個(gè)

    • 異步函數(shù)并發(fā)隊(duì)列:

      1、開啟線程,在當(dāng)前線程執(zhí)行任務(wù)

      2、任務(wù)異步執(zhí)行,沒(méi)有順序,CPU調(diào)度有關(guān)

    - (void)testDemo
    {
        dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        // 耗時(shí)
        dispatch_async(queue, ^{
            NSLog(@"2");
            dispatch_async(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
        // 打印: 1 5 2 4 3 
        // 因?yàn)閐ispatch_async不堵塞外部線程
        // 假設(shè):一個(gè)通道代表串行,多個(gè)通道代表并發(fā),一輛車代表一個(gè)線程,每個(gè)通道有每輛車的出貨的順序
        // 個(gè)人理解:
        //        車a:拉1,拉異步代碼塊:“我有車你先走”,拉5,其中異步代碼塊產(chǎn)生車b
        //        車b:拉2,拉異步代碼塊:“我有車你先走”,拉4,其中異步代碼塊產(chǎn)生車c
        //        車c:拉3
        //
        //
        //
    }
    
    - (void)testDemo2
    {
        // 串行隊(duì)列
        dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_SERIAL);
        NSLog(@"1");
        // 異步函數(shù)
        dispatch_async(queue, ^{
            NSLog(@"2");
            // 同步
            dispatch_sync(queue, ^{
               //NSLog(@"3");
            });
            // 這個(gè)地方?jīng)]有代碼一樣會(huì)產(chǎn)生死鎖
            //NSLog(@"4");
        });
        NSLog(@"5");
        // 152
        // 個(gè)人理解:
        //         串行隊(duì)列說(shuō)明只有一條通道,異步函數(shù)會(huì)創(chuàng)建多輛車,通道有每輛車的出貨的順序
        //         以下是計(jì)劃:
        //            1、車a拉1,車a拉異步代碼塊,異步代碼塊說(shuō):“你先走,我自己有車”,車a拉了5走了
        //            2、異步代碼塊中創(chuàng)建車b
        //            3、車b拉2,車b拉同步代碼塊,車b拉4
        //         實(shí)際情況:
        //            車b在拉同步代碼塊的時(shí)候被告知:“我沒(méi)車,你得拉3,不然你啥也別干了”
        //            車b:“通道要求我得先把4拉了”
        //         于是:
        //            產(chǎn)生死鎖,3和4誰(shuí)也不拉
    }
    
    - (void)testDemo1
    {
        dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        dispatch_async(queue, ^{
            NSLog(@"2");
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
      
        //打印 :1 5 2 3 4
      
        // 個(gè)人理解:
        //         并行隊(duì)列說(shuō)明有多條通道,同步函數(shù)會(huì)創(chuàng)建一輛車,每個(gè)通道有出貨的順序
        //         以下是計(jì)劃:
        //            1、車a拉1,車a拉異步代碼塊,異步代碼塊說(shuō):“你先走,我自己有車”,車a拉了5走了
        //            2、異步代碼塊中創(chuàng)建車b
        //            3、車b拉2,車b拉同步代碼塊,車b拉4
        //            4、同步代碼塊說(shuō):“你得先拉3,不然你啥也別干了”
        //            5、車b說(shuō):“當(dāng)前通道要求我先拉4,我換個(gè)通道把3拉了再回來(lái)拉4”
    }
    
  • 死鎖

    • 主線程因?yàn)槟阃胶瘮?shù)的原因等著先執(zhí)行任務(wù)
    • 主隊(duì)列等著主線程的任務(wù)執(zhí)行完畢再執(zhí)行自己的任務(wù)
    • 主隊(duì)列和主線程相互等待會(huì)造成死鎖
  • 特殊隊(duì)列

    系統(tǒng)加載時(shí)候的默認(rèn)隊(duì)列:

    1. dispatch_get_global_queue(0, 0);

      全局隊(duì)列:并發(fā)隊(duì)列

      不建議用

      • 為了方便程序員的使用,蘋果提供了全局隊(duì)列
      • 在使用多線程開發(fā)時(shí),如果對(duì)隊(duì)列沒(méi)有特殊需求,在執(zhí)行異步任務(wù)時(shí),可以直接使用全局隊(duì)列
    2. dispatch_get_main_queue();

      主隊(duì)列:串行隊(duì)列

      • 專門用來(lái)在主線程上調(diào)度任務(wù)的隊(duì)列
      • 不會(huì)開啟線程
      • 如果當(dāng)前主線程正在有任務(wù)執(zhí)行,那么無(wú)論主隊(duì)列中當(dāng)前被添加了什么任務(wù),都不會(huì)被調(diào)度
        int a = 0; 
        while (a<10) 
        { 
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                a++;
            });
        }
    
    //問(wèn)題1:
    //     a會(huì)報(bào)錯(cuò),為什么?
    //     
    //問(wèn)題2:
    //     a會(huì)輸出什么值?
    //     >=10
    //     原因:資源搶奪
    //          創(chuàng)建很多條線程,都會(huì)操作a
    //問(wèn)題3:  
    //     輸出a最后的值該怎么修改?  
    //    
    
        __block int a = 0; //問(wèn)題1修復(fù)
          //把a(bǔ)從棧區(qū)copy成一個(gè)struct(中有a的指針地址和a的值)
          //block中會(huì)修改newA
        while (a<10) 
        { 
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                a++;
            });
        }
    
          //問(wèn)題3
          //最后
          // 主線程 -- 堵塞 -- I/O -- 代碼執(zhí)行的耗時(shí) -- 相當(dāng)于一個(gè)同步效果
          // 堵塞 -- 給你足夠的時(shí)間 -- 把前面的任務(wù)執(zhí)行完畢之后 -- 往隊(duì)列加入任務(wù) -- 再執(zhí)行
        NSLog(@"主線程%d",a);//此時(shí)打印的也不是真的值
          dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"最后%d",a);
        });
    

二、GCD的應(yīng)用

  1. 柵欄函數(shù)

    • 一定要是自定義的并發(fā)隊(duì)列,不然沒(méi)有效果

      如果柵欄函數(shù)堵塞了全局并發(fā)隊(duì)列那系統(tǒng)就GG了

    • dispatch_barrier_async和dispatch_barrier_sync

      • dispatch_barrier_async 前面的任務(wù)執(zhí)行完畢才會(huì)來(lái)到這里,不影響隊(duì)列外任務(wù)的執(zhí)行
      • dispatch_barrier_sync 作用相同,但是這個(gè)會(huì)堵塞線程,影響后面的任務(wù)執(zhí)行,包括隊(duì)列外的任務(wù)
    • 柵欄函數(shù)只能控制同一并發(fā)隊(duì)列 --- 不夠優(yōu)秀的地方 --- 不利于封裝

  2. 調(diào)度組

    控制任務(wù)執(zhí)行順序

    • dispatch_group_create 創(chuàng)建組

      dispatch_group_async 進(jìn)組任務(wù)

      dispatch_group_notify 進(jìn)組任務(wù)執(zhí)行完畢通知

      dispatch_group_wait 進(jìn)組任務(wù)執(zhí)行等待時(shí)間

    • dispatch_group_enter 進(jìn)組

      dispatch_group_leave 出組

      注意搭配使用

  3. 信號(hào)量dispatch_semaphore_t

  4. Dispatch_Source

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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