iOS多線程實(shí)現(xiàn)——GCD的基本使用

今天和大家一起來討論一下GCD的基本使用,有疏忽的地方,還望各位不吝賜教。


一、多線程簡(jiǎn)介

1、多線程相關(guān)的概念

進(jìn)程:

1、進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序;
2、每個(gè)進(jìn)程是獨(dú)立的,每個(gè)進(jìn)程運(yùn)行在專用且受保護(hù)的內(nèi)存空間中。

線程:

1、一個(gè)進(jìn)程執(zhí)行任務(wù),必須依賴線程(一個(gè)進(jìn)程至少有一條線程);
2、進(jìn)程的所有任務(wù)都在線程中執(zhí)行。

線程的串行:

1、一個(gè)線程中任務(wù)的執(zhí)行是串行的
2、如果要在一個(gè)線程中執(zhí)行多個(gè)任務(wù),只能一個(gè)一個(gè)按順序執(zhí)行,即同一時(shí)間一個(gè)線程只能執(zhí)行一個(gè)任務(wù);

線程和進(jìn)程的比較:

1、線程是CPU調(diào)用(執(zhí)行任務(wù))的最小單位。
2、進(jìn)程是CPU分配資源的最小單位。
3、一個(gè)程序可以對(duì)應(yīng)多個(gè)進(jìn)程,一個(gè)進(jìn)程中可以有多個(gè)線程,但至少有一個(gè)。
4、同一個(gè)進(jìn)程中的線程共享進(jìn)程的資源。

多線程:

一個(gè)進(jìn)程中可以開啟多條線程的,每一條線程可以并行(同時(shí))執(zhí)行不同的任務(wù)。其實(shí)在同一時(shí)間,CPU只能處理一條線程,多線程的并發(fā)(同時(shí))執(zhí)行,其實(shí)是一種假象,只不過是快速的在多條線程之間進(jìn)行調(diào)度,也就是切換罷了。但是一般不建議開很多條線程,因?yàn)殚_線程越多,相應(yīng)的開銷就越大,對(duì)于CPU來說負(fù)擔(dān)就越重。

2、多線程的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):
1、能適當(dāng)?shù)奶岣叱绦虻膱?zhí)行效率;
2、能適當(dāng)?shù)奶岣哔Y源的利用率(包括CPU和內(nèi)存)。
缺點(diǎn):
1、創(chuàng)建線程是有開銷的,iOS 9下主要成本包括:內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)、棧空間(子線程512KB,主線程1MB,也可以使用-setStackSize:進(jìn)行設(shè)置,但是必須是4KB的倍數(shù),最小16KB),創(chuàng)建線程大約需要90毫秒的時(shí)間進(jìn)行創(chuàng)建。
2、如果開啟大量的線程,會(huì)降低程序的性能。
3、線程越多,CPU在線程上的開銷就越大。
4、程序設(shè)計(jì)更加復(fù)雜,包括在線程之間的通信和多線程之間的數(shù)據(jù)共享。

3、多線程在iOS中的應(yīng)用

主線程

一個(gè)iOS程序運(yùn)行后,默認(rèn)會(huì)開啟一條線程,稱為主線程或者UI線程。

主線程的主要作用

1、顯示\刷新UI界面;
2、處理UI事件(比如點(diǎn)擊事件,滾動(dòng)事件和拖拽事件等);
3、注意不要將耗時(shí)操作放到主線程匯總;
4、耗時(shí)操作會(huì)卡住主線程,嚴(yán)重影響UI的流暢度,造成不好的用戶體驗(yàn);
5、在開發(fā)過程中,注意將耗時(shí)操作放到子線程中進(jìn)行執(zhí)行。

二、多線程安全

當(dāng)多個(gè)線程同時(shí)訪問一塊資源的時(shí)候,會(huì)發(fā)生數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全的問題。
解決方案:互斥鎖
當(dāng)線程A訪問資源的時(shí)候加鎖,保證其他線程無法在線程A訪問期間進(jìn)行對(duì)本資源的操作,在線程A操作完成后,會(huì)打開添加的互斥鎖,之后其他線程可以按照同樣的方式進(jìn)行訪問資源。

    格式:
    @synchronized(鎖對(duì)象) {需要鎖定的代碼}
    使用方式:
    @synchronized(self) {}

注意點(diǎn):
1、鎖必須是全局唯一的,所以一般使用控制器作為標(biāo)識(shí)。
2、注意加鎖的前提條件,在多線程訪問同一塊資源的時(shí)候再添加。
3、加鎖是需要耗費(fèi)性能的。
4、加鎖的結(jié)果:線程同步(多條線程在同一條線上執(zhí)行,按照順序進(jìn)行執(zhí)行)
關(guān)于原子屬性和非原子屬性的解釋:
*atomic:
原子屬性,會(huì)為setter方法加鎖,默認(rèn)為atomic。
因?yàn)闀?huì)為setter方法加鎖,所以在使用的時(shí)候是線程安全的,但是會(huì)消耗大量的資源。
*nonatomic:
非原子屬性,不會(huì)為setter方法加鎖。
雖然nonatomic是不安全的,但是出現(xiàn)多個(gè)線程同時(shí)訪問一塊資源的情況很少,為了保證效率,所以一般使用nonatomic。
建議:
盡量把這些爭(zhēng)奪資源的任務(wù)交給服務(wù)器端處理,減小移動(dòng)端的壓力。

三、iOS多線程實(shí)現(xiàn)方案

技術(shù)方案 簡(jiǎn)介 語言 線程生命周期 使用頻率
pthread 一套通用的多線程API
可移植,適用于多個(gè)平臺(tái)
使用難度大
C 程序員管理 幾乎不用
NSThread 使用更加面向?qū)ο?br>簡(jiǎn)單易用,可以直接操作線程對(duì)象 OC 程序員管理 偶爾使用
GCD 為了替代NSThread等的線程技術(shù)
充分利用設(shè)備的多核
C 自動(dòng)管理 經(jīng)常使用
NSOperation 基于GCD
相比GCD多了一些更簡(jiǎn)單實(shí)用的功能
更加面向?qū)ο?/td>
OC 自動(dòng)管理 經(jīng)常使用

四、GCD多線程實(shí)現(xiàn)方案

1、GCD簡(jiǎn)介

全稱是Grand Central Dispatch,是一個(gè)純C語言的庫,是蘋果公司為多核的并行運(yùn)算提出的解決方案。

2、GCD優(yōu)勢(shì)

1、GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核和四核)
2、GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程,調(diào)度線程和銷毀線程)
3、程序員不需要手動(dòng)編寫管理線程的任何代碼,只需要告訴GCD想要執(zhí)行什么任務(wù)

3、任務(wù)和隊(duì)列

任務(wù):執(zhí)行的操作統(tǒng)稱為任務(wù)
隊(duì)列:用來存放任務(wù),并且調(diào)度任務(wù)在那些線程中執(zhí)行

4、使用步驟

1、定制任務(wù)
確定想做的任務(wù)
2、將任務(wù)添加到隊(duì)列中
GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出來,放到對(duì)應(yīng)的線程中執(zhí)行
任務(wù)的取出遵循隊(duì)列的FIFO的原則:先進(jìn)先出,后進(jìn)后出

5、執(zhí)行任務(wù)的方式

GCD中有兩個(gè)用來執(zhí)行任務(wù)的常用函數(shù)

  • 同步函數(shù)
    同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
  • 異步函數(shù)
    異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力

6、隊(duì)列的類型

GCD中隊(duì)列分為兩大類型

  • 并發(fā)隊(duì)列(Concurrent Dispatch Queue)
    可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
  • 串行隊(duì)列 (Serial Dispatch Queue)
    可以讓任務(wù)一個(gè)接著一個(gè)的執(zhí)行(一個(gè)執(zhí)行完之后,再執(zhí)行下一個(gè)任務(wù))

7、術(shù)語的相關(guān)解釋

  • 同步和異步主要影響的是能不能開啟新的線程 這里指的是函數(shù)
    同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
    異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
  • 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式 這里指的是隊(duì)列
    并發(fā):可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
    串行:可以讓任務(wù)一個(gè)接著一個(gè)的執(zhí)行(一個(gè)執(zhí)行完之后,再執(zhí)行下一個(gè)任務(wù))

五、pthread簡(jiǎn)單使用

因?yàn)閜thread用的不多,所以簡(jiǎn)單提一下,關(guān)于其創(chuàng)建線程的使用如下:

   // 1、創(chuàng)建線程對(duì)象
    pthread_t thread;
    
    // 2、創(chuàng)建線程
    /*
     * 第一個(gè)參數(shù):線程對(duì)象,傳遞地址
     * 第二個(gè)參數(shù):線程的屬性 NULL
     * 第三個(gè)參數(shù):指向函數(shù)的指針
     * 第四個(gè)參數(shù):函數(shù)需要接受的參數(shù)
     */
    pthread_create(&thread, NULL, task, NULL);
    // 3、task實(shí)現(xiàn)
    void *task(void *param){

        NSLog(@"%@",[NSThread currentThread]);
        return NULL;
    }

六、GCD簡(jiǎn)單使用

創(chuàng)建并發(fā)隊(duì)列的方式

1、create創(chuàng)建并發(fā)隊(duì)列(主動(dòng)創(chuàng)建)

    /*
     * 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
     * 第二個(gè)參數(shù):隊(duì)列的類型
     * DISPATCH_QUEUE_CONCURRENT 并發(fā)
     * DISPATCH_QUEUE_SERIAL 串行
     */
    
    dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_CONCURRENT);
    

2、獲取全局并發(fā)隊(duì)列(系統(tǒng)中本身存在,拿過來用就行了)

  /* 和DISPATCH_QUEUE_CONCURRENT創(chuàng)建的并發(fā)隊(duì)列 是等價(jià)的
   * 第一個(gè)參數(shù):優(yōu)先級(jí) 四種 一般用默認(rèn)的即可
        #define DISPATCH_QUEUE_PRIORITY_HIGH 2
        #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
        #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
        #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 最低的優(yōu)先級(jí)
   * 第二個(gè)參數(shù):系統(tǒng)說明是給未來使用的,使用的時(shí)候傳參為0即可。
   */
  
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  

創(chuàng)建串行隊(duì)列的方式

1、create創(chuàng)建串行隊(duì)列(主動(dòng)創(chuàng)建)

    /*
     * 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
     * 第二個(gè)參數(shù):隊(duì)列的類型
     * DISPATCH_QUEUE_CONCURRENT 并發(fā)
     * DISPATCH_QUEUE_SERIAL 串行 或者傳遞NULL 
     */
    
    dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_SERIAL);
    

2、使用主隊(duì)列(跟主線程相關(guān)聯(lián)的隊(duì)列)

    /*
   * 主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列,放在主隊(duì)列中的任務(wù)都會(huì)放在主線程中進(jìn)行執(zhí)行。
   * 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
   * 第二個(gè)參數(shù):隊(duì)列的類型
   * DISPATCH_QUEUE_CONCURRENT 并發(fā)
   * DISPATCH_QUEUE_SERIAL 串行 或者傳遞NULL 
   */
  

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

1、異步函數(shù) + 并發(fā)隊(duì)列: 會(huì)開啟多條線程,隊(duì)列中的任務(wù)是異步(并發(fā))執(zhí)行

    // 1.創(chuàng)建隊(duì)列
    /*
     * 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
     * 第二個(gè)參數(shù):隊(duì)列的類型
     * DISPATCH_QUEUE_CONCURRENT 并發(fā)
     * DISPATCH_QUEUE_SERIAL 串行
     */
    
    dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_CONCURRENT);
    
    // 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
    /* 
     * 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
     * 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
     * 注意:
        并不是有幾個(gè)任務(wù)就開幾條線程,具體開幾條線程由系統(tǒng)進(jìn)行衡量!
     */
    
    dispatch_async(queue, ^{
        NSLog(@"1---------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2---------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3---------%@",[NSThread currentThread]);
    });
    // 打印結(jié)果:
        3---------<NSThread: 0x60800007e880>{number = 5, name = (null)}
        1---------<NSThread: 0x600000078700>{number = 3, name = (null)}
        2---------<NSThread: 0x60800007e2c0>{number = 4, name = (null)}

2、異步函數(shù) + 串行隊(duì)列: 會(huì)開啟一條線程,隊(duì)列中的任務(wù)是同步(串行)執(zhí)行的

    // 1.創(chuàng)建隊(duì)列
    /*
     * 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
     * 第二個(gè)參數(shù):隊(duì)列的類型
     * DISPATCH_QUEUE_CONCURRENT 并發(fā)
     * DISPATCH_QUEUE_SERIAL 串行
     */
    
    dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_SERIAL);
    
    // 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
    /*
     * 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
     * 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
     */
    
    dispatch_async(queue, ^{
        NSLog(@"1---------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2---------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3---------%@",[NSThread currentThread]);
    });
    // 打印結(jié)果:
        1---------<NSThread: 0x60000007d180>{number = 3, name = (null)}
        2---------<NSThread: 0x60000007d180>{number = 3, name = (null)}
        3---------<NSThread: 0x60000007d180>{number = 3, name = (null)}

3、同步函數(shù) + 并發(fā)隊(duì)列:不會(huì)開線程,任務(wù)是串行執(zhí)行的

    // 1.創(chuàng)建隊(duì)列
    /*
     * 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
     * 第二個(gè)參數(shù):隊(duì)列的類型
     * DISPATCH_QUEUE_CONCURRENT 并發(fā)
     * DISPATCH_QUEUE_SERIAL 串行
     */
    
    dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_CONCURRENT);
   
    // 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
    /*
     * 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
     * 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
     */
    
    dispatch_sync(queue, ^{
        NSLog(@"1---------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"2---------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"3---------%@",[NSThread currentThread]);
    });
    // 打印結(jié)果:
        1---------<NSThread: 0x60000007e800>{number = 1, name = main}
        2---------<NSThread: 0x60000007e800>{number = 1, name = main}
        3---------<NSThread: 0x60000007e800>{number = 1, name = main}

4、同步函數(shù) + 串行隊(duì)列:不會(huì)開線程,任務(wù)是串行執(zhí)行的(效果和3同)

    // 1.創(chuàng)建隊(duì)列
    /*
     * 第一個(gè)參數(shù):c語言的字符串,標(biāo)簽
     * 第二個(gè)參數(shù):隊(duì)列的類型
     * DISPATCH_QUEUE_CONCURRENT 并發(fā)
     * DISPATCH_QUEUE_SERIAL 串行
     */
    
    dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_SERIAL);
    
    // 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
    /*
     * 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
     * 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
     */
    
    dispatch_sync(queue, ^{
        NSLog(@"1---------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"2---------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"3---------%@",[NSThread currentThread]);
    });
    // 打印結(jié)果:
        1---------<NSThread: 0x60000007e800>{number = 1, name = main}
        2---------<NSThread: 0x60000007e800>{number = 1, name = main}
        3---------<NSThread: 0x60000007e800>{number = 1, name = main}

5、異步函數(shù) + 主隊(duì)列:不會(huì)開線程,任務(wù)是串行執(zhí)行的

    // 1.獲得主隊(duì)列

    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
    /*
     * 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
     * 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
     */
    
    dispatch_async(queue, ^{
        NSLog(@"1---------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2---------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3---------%@",[NSThread currentThread]);
    });
    // 打印結(jié)果:
        1---------<NSThread: 0x60000007e800>{number = 1, name = main}
        2---------<NSThread: 0x60000007e800>{number = 1, name = main}
        3---------<NSThread: 0x60000007e800>{number = 1, name = main}

6、同步函數(shù)(立刻馬上執(zhí)行) + 主隊(duì)列:死鎖

    // 1.獲得主隊(duì)列

    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中
    /*
     * 第一個(gè)參數(shù):要執(zhí)行的隊(duì)列
     * 第二個(gè)參數(shù):要執(zhí)行的任務(wù)
     */
    
    dispatch_sync(queue, ^{
        NSLog(@"1---------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"2---------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"3---------%@",[NSThread currentThread]);
    });
    
    // 說明:
        主隊(duì)列的特點(diǎn):如果主隊(duì)列發(fā)現(xiàn)當(dāng)前主線程正在執(zhí)行任務(wù),會(huì)等到主線程執(zhí)行完當(dāng)前任務(wù)后,再調(diào)度主隊(duì)列中的任務(wù),如果在子線程中使用以下方法就可以避免死鎖
        同步函數(shù):立刻馬上執(zhí)行,我不執(zhí)行完,后面的誰都別想執(zhí)行
        異步函數(shù):如果我不執(zhí)行完,后面的也可以執(zhí)行

各種隊(duì)列執(zhí)行總結(jié)

并發(fā)隊(duì)列 手動(dòng)創(chuàng)建串行隊(duì)列 主隊(duì)列
同步 沒有開啟新線程
串行執(zhí)行任務(wù)
沒有開啟新線程
串行執(zhí)行任務(wù)
沒有開啟新線程
串行執(zhí)行任務(wù)
異步 開啟新線程
并發(fā)執(zhí)行任務(wù)
開啟新線程
串行執(zhí)行任務(wù)
沒有開啟新線程
串行執(zhí)行任務(wù)

七、GCD線程之間的通信

// 1、獲取全局并發(fā)隊(duì)列

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

// 2、封裝任務(wù) > 將任務(wù)添加到隊(duì)列中

    dispatch_async(queue, ^{
        // 在這里執(zhí)行子線程中相關(guān)耗時(shí)的操作
        /*
             例如網(wǎng)絡(luò)請(qǐng)求或者下載任務(wù)
          */
        dispatch_async(dispatch_get_main_queue(), ^{
            
            // 在這里進(jìn)行和主線程的通信,主要是刷新UI的相關(guān)操作,這里用同步函數(shù)也是可以的,并不會(huì)造成死鎖。
            
        });
        
    });

八、GCD常用函數(shù)

1、延遲執(zhí)行

    1)延遲執(zhí)行的第一種方法
    [self performSelector:@selector(task) withObject:nil afterDelay:2.0];
    
    2)延遲執(zhí)行的第二種方法
     [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];

    3)延遲執(zhí)行的第二種方法 
    /* 主要可以控制延遲執(zhí)行的操作在哪個(gè)隊(duì)列中執(zhí)行 dispatch_get_main_queue() 修改此參數(shù)即可
     * 第一個(gè)參數(shù):DISPATCH_TIME_NOW 從現(xiàn)在開始計(jì)算時(shí)間
     * 第二個(gè)參數(shù):延遲的時(shí)間 GCD的時(shí)間單位 納秒
     * 第三個(gè)參數(shù):dispatch_get_main_queue 主隊(duì)列
     */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        NSLog(@"delay----%@",[NSThread currentThread]);
        
    });

2、一次性執(zhí)行
使用場(chǎng)景:?jiǎn)卫J剑ㄔ谝院蟮姆窒碇袝?huì)提到。。。。。。)

    // static 靜態(tài)全局變量,作用域是整個(gè)應(yīng)用程序的生命周期
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"once-----");
    });
     注意:
             一次性代碼不能放在懶加載中 會(huì)出問題
             因?yàn)榇舜a只執(zhí)行一次,在第一次創(chuàng)建的時(shí)候會(huì)把對(duì)象返回,但是在第二次創(chuàng)建對(duì)象的時(shí)候,不會(huì)再執(zhí)行一次性代碼,所以對(duì)象返回值為空。

3、GCD的柵欄函數(shù)
控制并發(fā)隊(duì)列中異步執(zhí)行任務(wù)的執(zhí)行順序。

    // 獲得全局并發(fā)隊(duì)列
    // 柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
    //    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    dispatch_queue_t queue = dispatch_queue_create("en", DISPATCH_QUEUE_CONCURRENT);
    // 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"1--------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2--------%@",[NSThread currentThread]);
    });
    // 柵欄函數(shù) 先執(zhí)行1、2,關(guān)于1、2的先后是不能確定的,再執(zhí)行3。
    dispatch_barrier_async(queue, ^{
        NSLog(@"+++++++++++++++++++");
    });
    dispatch_async(queue, ^{
        NSLog(@"3--------%@",[NSThread currentThread]);
    });
      // 打印結(jié)果:
        1--------<NSThread: 0x6000000754c0>{number = 3, name = (null)}
        2--------<NSThread: 0x60000006a340>{number = 4, name = (null)}
        +++++++++++++++++++
        3--------<NSThread: 0x60000006a340>{number = 4, name = (null)}

4、GCD快速迭代
迭代其實(shí)就是遍歷,說白了就是一個(gè)循環(huán)。

    // 1)快速迭代的第一種方法
        for循環(huán),但是for循環(huán)是同步執(zhí)行的。

    // 2)快速迭代的第二種方法
    /* 主線程和子線程一起完成任務(wù) 任務(wù)的執(zhí)行是并發(fā)的
     * 第一個(gè)參數(shù):迭代的次數(shù)
     * 第二個(gè)參數(shù):并發(fā)隊(duì)列 只能傳并發(fā)隊(duì)列,如果傳主隊(duì)列,死鎖,如果傳手動(dòng)創(chuàng)建的串行隊(duì)列跟for循環(huán)效果一樣
     * 第三個(gè)參數(shù):索引
     */
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
        
        NSLog(@"%@-----%zu",[NSThread currentThread],index);
        
    });

5、GCD定時(shí)器

    /** 定時(shí)器 */
    @property (nonatomic, strong) dispatch_source_t timer;
    // 創(chuàng)建timer
    /* GCD中的定時(shí)器是絕對(duì)精準(zhǔn)的,不會(huì)受到RunLoop的影響!
     * 第一個(gè)參數(shù):類型 DISPATCH_SOURCE_TYPE_TIMER
     * 第二個(gè)參數(shù):描述信息
     * 第三個(gè)參數(shù):更詳細(xì)的描述信息
     * 第四個(gè)參數(shù):隊(duì)列 決定GCD的timer在那個(gè)線程中執(zhí)行
     */
    // 以局部變量的形式進(jìn)行定義,timer不會(huì)執(zhí)行的,因?yàn)樵趫?zhí)行之前已經(jīng)被釋放了
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    /*
     * 第一個(gè)參數(shù):定時(shí)器對(duì)象
     * 第二個(gè)參數(shù):DISPATCH_TIME_NOW 起始時(shí)間
     * 第三個(gè)參數(shù):間隔時(shí)間
     * 第四個(gè)參數(shù):精準(zhǔn)度 允許誤差,此處傳0即可
     */
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
    //  設(shè)置定時(shí)器的任務(wù)
    dispatch_source_set_event_handler(self.timer, ^{
        
        NSLog(@"執(zhí)行任務(wù)");
        
    });
    // 開啟定時(shí)器
    dispatch_resume(self.timer);

九、GCD隊(duì)列組的使用

跟柵欄函數(shù)相類似,也是用來控制并發(fā)隊(duì)列中異步執(zhí)行任務(wù)的執(zhí)行順序的。

    // 1、獲得全局并發(fā)隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 2、創(chuàng)建隊(duì)列組
    dispatch_group_t group = dispatch_group_create();
    // 3、異步函數(shù)
    /* 這個(gè)函數(shù)做了什么?
      1、封裝任務(wù) 
      2、將任務(wù)添加到隊(duì)列中
      3、會(huì)監(jiān)聽任務(wù)執(zhí)行情況,通知group
    */
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"1---------%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"2---------%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"3---------%@",[NSThread currentThread]);
    });
    // 4、攔截通知,當(dāng)所有的隊(duì)列組中的任務(wù)執(zhí)行完畢以后會(huì)執(zhí)行這個(gè)方法
    dispatch_group_notify(group, queue, ^{
        NSLog(@"end--------end");
    });
    // 打印結(jié)果:
        2---------<NSThread: 0x60800007a8c0>{number = 7, name = (null)}
        1---------<NSThread: 0x60800007a800>{number = 6, name = (null)}
        3---------<NSThread: 0x608000078980>{number = 8, name = (null)}
        end--------end

另外一種GCD隊(duì)列組的實(shí)現(xiàn)方式(以前使用的)

    // 獲得全局并發(fā)隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 創(chuàng)建隊(duì)列組
    dispatch_group_t group = dispatch_group_create();
    // 此方法之后執(zhí)行的執(zhí)行的異步任務(wù)會(huì)被納入到隊(duì)列組的監(jiān)聽范圍內(nèi)
    // 配對(duì)使用
    dispatch_group_enter(group);
    
    dispatch_group_async(group, queue, ^{
       NSLog(@"1---------%@",[NSThread currentThread]);
        
        // 離開群組
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"2---------%@",[NSThread currentThread]);
        
        // 離開群組
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"3---------%@",[NSThread currentThread]);
        
        // 離開群組
        dispatch_group_leave(group);
    });
    
    // 當(dāng)所有的隊(duì)列組中的任務(wù)執(zhí)行完畢以后會(huì)執(zhí)行這個(gè)方法
    // 內(nèi)部本身是異步的,不會(huì)阻塞
    dispatch_group_notify(group, queue, ^{
        
        NSLog(@"end--------end");
    });

    // 其他的監(jiān)聽方法 DISPATCH_TIME_FOREVER 死等 等到所有任務(wù)都執(zhí)行完畢才執(zhí)行這個(gè)方法
    // 這里是阻塞的,如果下面方法沒有執(zhí)行,以后的方法不會(huì)執(zhí)行的。
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

十、GCD異步函數(shù)的補(bǔ)充

GCD異步函數(shù)有兩種實(shí)現(xiàn)方式,具體的異同比較如下:

 1、block 實(shí)現(xiàn)
 // dispatch_async(<dispatch_queue_t  _Nonnull queue>, <^(void)block>)
 2、方法調(diào)用的方式實(shí)現(xiàn)
    /*
     * 第一個(gè)參數(shù):隊(duì)列
     * 第二個(gè)參數(shù):參數(shù)
     * 第三個(gè)參數(shù):要調(diào)用的函數(shù)名稱
     */
    dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
    dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
    dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
//  調(diào)用函數(shù)的實(shí)現(xiàn)
void task(void *params){
    NSLog(@"%s--------%@",__func__,[NSThread currentThread]);
}

寫在最后的話:關(guān)于GCD的知識(shí)今天就分享到這里,關(guān)于iOS多線程實(shí)現(xiàn)方面的問題歡迎大家和我交流,共同進(jì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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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