iOS 多線程-GCD任務+隊列

多線程是開發中長用到的技術,特別是對於項目邏輯複雜的程序,更需要多線程的幫助。

在學習多線程之前需要來了解幾個基本概念:
1、進程:每一個運行的程序都是一個進程。
2、線程:線程是進程中的一個執行單元,每一個進程中都至少有一個線程。

多線程

多線程就是指在進程中有多個線程處理任務,多線程的目的是爲了提高CPU的利用率。

在iOS的開發中使用到的多線程技術有如下3種:
1、NSthread
2、GCD
3、NSOperation

GCD

GCD(Grand Central Dispatch)大中央調度,是apple開發的一個多核開發的解決辦法。主要用於優化程序以支持多核處理器。

GCD的優點:
1、可用於多核的並行運算。
2、自動利用CPU更多的內核。
3、自動管理線程生命週期。

GCD之前先來了解多線程中的一些概念:
1、任務:需要執行的操作,程序中每一個需要線程執行的操作都是一個任務。
在多線程開發中對執行任務有兩種方式:同步執行異步執行,同步和異步的區別在於是否等待隊列中的任務執行完畢和是否開闢新的線程

同步執行(sync)

同步添加任務到指定隊列,在添加任務執行結束之前,一直等待,直到隊列中的任務執行完成再繼續執行。
不開闢新的線程。

異步執行(async)

異步添加任務到指定隊列中,不會出現等待,會繼續執行。
開闢新的線程。

舉個例子:
現在有A和B兩個任務。
同步執行是的方式是先執行A,等A執行完成後再執行B。
異步執行是在執行A的時候也可以開始執行B,執行B的時候也可以執行A,兩個任務不需要等待關係。

上面說到異步執行可以開闢新的線程,但是在實際的使用中是否開闢新的線程不是一定的,這個和添加到的隊列有關。

隊列(dispatch Queue)

隊列是用來放置任務的,隊列是一種特殊的線性表,採用FIFO(先進先出)的原則,及任務添加到隊列是從隊列尾部添加,而讀取任務執行是從隊列的頭部開始讀取。例如排隊去食堂打飯,新來的人要排在隊尾,先排隊的先打飯。

GCD中有兩種隊列:串行隊列併發隊列
兩種隊列都是採用FIFO的原則,區別在於執行順序不同和開闢線程數不同。

串行隊列(Serial Dispatch Queue)

每次只有一個任務被執行,當被執行的任務執行完畢在執行下一個任務。隊列中只有一個線程。

併發隊列(Concurrent Dispatch Queue)

可以讓多個任務同時執行,可以開闢多個線程來執行任務。

GCD的使用

GCD是C語言開發的,在使用中也是使用C語言的調用方式。

GCD的使用方式很簡單一共分爲兩步:
第一步:創建隊列(串行隊列、異步隊列)
第二步:將任務添加到隊列中,然後系統會根據任務類型執行任務(同步執行、異步執行)。

1、創建隊列

/**
     GCD創建線程的方法:dispatch_queue_create()
     這個方法中有兩個參數:label和attr
     label:線程隊列的唯一標識,簡單理解爲隊列名稱,可以爲空
     attr:識別隊列是串行隊列還是併發隊列,
        DISPATCH_QUEUE_SERIAL: 串行隊列
        DISPATCH_QUEUE_CONCURRENT:併發隊列
     */
    //創建一個串行隊列
    dispatch_queue_t serialQueue = dispatch_queue_create("dispatchName", DISPATCH_QUEUE_SERIAL);
    //創建一個併發隊列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("dispatchName", DISPATCH_QUEUE_CONCURRENT);

在串行隊列中GCD默認提供了一個主隊列(Main Dispatch Queue)
主隊列並不是特殊隊列,實質上是一個普通的串行隊列,只是默認情況下當前任務是放在主隊列中執行。這個隊列會將任務插入到主線程執行。

    //GCD獲取主隊列的方式
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

在併發隊列中GCD提供了一個全局併發隊列(Global Dispatch Queue)

    //GCD獲取x全局併發隊列
    /**
     獲取全局併發隊列的方法:dispatch_get_global_queue()
     這個方法有兩個參數:identifier和flags
        identifier表示隊列的優先級有下面4種:
            DISPATCH_QUEUE_PRIORITY_HIGH 2                                        高優先級
            DISPATCH_QUEUE_PRIORITY_DEFAULT 0                                  默認
            DISPATCH_QUEUE_PRIORITY_LOW (-2)                                      低優先級
            DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN        後臺
        flags是爲了以後拓展用的,都寫0就可以了
     */
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

2、創建任務

任務的創建有兩種方式(同步和異步),所以GCD提供了兩種方式任務的創建方法:同步執行任務創建方法:dispatch_sync,異步執行任務創建方法:dispatch_async

	//創建同步執行任務
   /**
    dispatch_sync()有一個參數queue和一個block
       queue:表示需要將任務添加到的隊列   本例中是將任務添加到了上面創建的一個串行隊列中
       block中是需要執行的任務
    */
   dispatch_sync(serialQueue, ^{
       //需要執行的任務
   });

   //創建異步執行任務
   /**
    dispatch_async()有一個參數queue和一個block
       queue:表示需要將任務添加到的隊列   本例中是將任務添加到了上面創建的一個併發隊列中
       block中是需要執行的任務
    */
   dispatch_async(concurrentQueue, ^{
       //需要執行的任務
   });

問題

到現在,一共出現了兩個任務概念和兩個隊列概念。在開發中需要將兩種任務執行方式和兩種隊列進行組合。簡單來說就是我們需要先將任務的執行方式確定然後在添加到隊列中。
在組合的過程中就會出現4中情況:
1、同步任務+串行隊列
2、同步任務+併發隊列
3、異步任務+串行隊列
4、異步任務+併發隊列
由於主隊列是串行隊列,全局併發隊列是併發隊列,這裏不特殊列舉了。最後在測試。
這4中組合到底會是什麼樣的執行順序呢?帶着這個疑問,後面來代碼測試。

1、同步任務+串行隊列

    //1、創建一個串行隊列,並添加同步執行任務
    dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    //2、創建幾個同步執行的任務
    dispatch_sync(queue, ^{
        //任務1
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"syncSerial1 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_sync(queue, ^{
        //任務2
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"syncSerial2 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_sync(queue, ^{
        //任務3
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"syncSerial3 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_sync(queue, ^{
        //任務4
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"syncSerial4 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });

打印結果:

2020-03-18 14:48:39.072215+0800 GCDdemo[85268:1300199] syncSerial1 =====  <NSThread: 0x600002fa2300>{number = 1, name = main}
2020-03-18 14:48:41.073474+0800 GCDdemo[85268:1300199] syncSerial2 =====  <NSThread: 0x600002fa2300>{number = 1, name = main}
2020-03-18 14:48:43.074822+0800 GCDdemo[85268:1300199] syncSerial3 =====  <NSThread: 0x600002fa2300>{number = 1, name = main}
2020-03-18 14:48:45.075994+0800 GCDdemo[85268:1300199] syncSerial4 =====  <NSThread: 0x600002fa2300>{number = 1, name = main}

從上面的打印結果可以看到每一個任務執行的時間間隔2秒,也就是模擬耗時操作的2秒。任務是順序執行

2、同步任務+併發隊列

    //1、創建一個併發隊列,並添加同步執行任務
    dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_CONCURRENT);
    //2、創建幾個同步執行的任務
    dispatch_sync(queue, ^{
        //任務1
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"syncConcurrent1 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_sync(queue, ^{
        //任務2
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"syncConcurrent2 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_sync(queue, ^{
        //任務3
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"syncConcurrent3 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_sync(queue, ^{
        //任務4
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"syncConcurrent4 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });

打印結果:

2020-03-18 14:50:00.318846+0800 GCDdemo[85314:1301623] syncConcurrent1 =====  <NSThread: 0x6000017e9040>{number = 1, name = main}
2020-03-18 14:50:02.319483+0800 GCDdemo[85314:1301623] syncConcurrent2 =====  <NSThread: 0x6000017e9040>{number = 1, name = main}
2020-03-18 14:50:04.321007+0800 GCDdemo[85314:1301623] syncConcurrent3 =====  <NSThread: 0x6000017e9040>{number = 1, name = main}
2020-03-18 14:50:06.322498+0800 GCDdemo[85314:1301623] syncConcurrent4 =====  <NSThread: 0x6000017e9040>{number = 1, name = main}

從上面的打印結果可以看到每一個任務執行的時間間隔2秒,也就是模擬耗時操作的2秒。任務是順序執行

3、異步任務+串行隊列

   //1、創建一個串行隊列,並添加異步執行任務
    dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    //2、創建幾個異步步執行的任務
    dispatch_async(queue, ^{
        //任務1
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"asyncSerial  1 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_async(queue, ^{
        //任務2
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"asyncSerial  2 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_async(queue, ^{
        //任務3
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"asyncSerial  3 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_async(queue, ^{
        //任務4
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"asyncSerial  4 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });

打印結果:

2020-03-18 14:53:45.095510+0800 GCDdemo[85430:1305376] asyncSerial  1 =====  <NSThread: 0x600002bc0640>{number = 4, name = (null)}
2020-03-18 14:53:47.097073+0800 GCDdemo[85430:1305376] asyncSerial  2 =====  <NSThread: 0x600002bc0640>{number = 4, name = (null)}
2020-03-18 14:53:49.101455+0800 GCDdemo[85430:1305376] asyncSerial  3 =====  <NSThread: 0x600002bc0640>{number = 4, name = (null)}
2020-03-18 14:53:51.106984+0800 GCDdemo[85430:1305376] asyncSerial  4 =====  <NSThread: 0x600002bc0640>{number = 4, name = (null)}

從上面的打印結果可以看到每一個任務執行的時間間隔2秒,也就是模擬耗時操作的2秒。任務是順序執行的。而且這裏的線程number都是4說明有一個新線程被開闢出來了。

4、異步任務+併發隊列

  //1、創建一個併發隊列,並添加異步執行任務
    dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_CONCURRENT);
    //2、創建幾個異步執行的任務
    dispatch_async(queue, ^{
        //任務1
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"asyncSerial  1 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_async(queue, ^{
        //任務2
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"asyncSerial  2 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_async(queue, ^{
        //任務3
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"asyncSerial  3 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });
    
    dispatch_async(queue, ^{
        //任務4
        [NSThread sleepForTimeInterval:2]; //模擬耗時操作
        NSLog(@"asyncSerial  4 =====  %@", [NSThread currentThread]);   //打印當先任務和線程
    });

打印結果:

2020-03-18 14:58:47.534261+0800 GCDdemo[85593:1309727] asyncSerial  3 =====  <NSThread: 0x6000030c9940>{number = 5, name = (null)}
2020-03-18 14:58:47.534261+0800 GCDdemo[85593:1309728] asyncSerial  2 =====  <NSThread: 0x6000030a6e40>{number = 8, name = (null)}
2020-03-18 14:58:47.534318+0800 GCDdemo[85593:1309732] asyncSerial  1 =====  <NSThread: 0x6000030b0ec0>{number = 7, name = (null)}
2020-03-18 14:58:47.534351+0800 GCDdemo[85593:1309729] asyncSerial  4 =====  <NSThread: 0x6000030905c0>{number = 4, name = (null)}

從上面的打印結果可以看到每一個任務執行結束的時間間隔不在是2秒,任務是併發執行的。而且每個人物的線程也都不一樣,說明開闢了新的線程,每個線程執行一個任務,異步執行,幾乎同時執行結束。

總結

從上面4中組合情況來看,只有異步執行+併發隊列纔會出現開闢出多個線程來執行任務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章