iOS多線程開發之離不開的GCD(上篇)

来源:http://www.cnblogs.com/beckwang0912/archive/2017/07/02/7100201.html
-Advertisement-
Play Games

一、GCD基本概念 GCD 全稱Grand Central Dispatch(大中樞隊列調度),是一套低層API,提供了⼀種新的方法來進⾏併發程式編寫。從基本功能上講,GCD有點像NSOperationQueue,他們都允許程式將任務切分為多個單一任務,然後提交⾄⼯作隊列來併發的或者串⾏的執行。GC ...


一、GCD基本概念

     GCD 全稱Grand Central Dispatch(大中樞隊列調度),是一套低層API,提供了⼀種新的方法來進⾏併發程式編寫。從基本功能上講,GCD有點像NSOperationQueue,他們都允許程式將任務切分為多個單一任務,然後提交⾄⼯作隊列來併發的或者串⾏的執行。GCD是C實現,⽐NSOpertionQueue更底層更高效,並且它不是Cocoa框架的一部分 併發任務會像NSOperationQueue那樣基於系統負載來合適地併發進⾏,而串⾏行隊列同一時間只執行單一任務,GCD的API很大程度上基於block。

    GCD併發編程的主要好處歸納

  • GCD可用於多核的並行運算
  • GCD會自動利用更多的CPU內核(比如雙核、四核)
  • GCD會自動管理線程的生命周期(創建線程、調度任務、銷毀線程)
  • 程式員只需要告訴GCD想要執行什麼任務,不需要編寫任何線程管理代碼

二、GCD如何實現

     GCD主要由隊列和任務兩部分來實現,蘋果官方對GCD是這樣說明的:開發者要做的只是定義想執行的任務並追加到適當的Dispatch Queue中。Dispatch Queue是執行處理的等待隊列,我們可以通過dispatch_async等API,在block語法中記述想要執行的處理並將其追加到Dispatch Queue中,Dispatch Queue是按照追加的順序進行處理(先進先出FIFO)。

     多線程執行過程就是把任務放在隊列中去執行的過程。那麼在這裡我們首先回顧一下基本概念:

   (一)進程/線程、任務/隊列

     

 

       

 

 

 

 

 

 

 

   (二)同步/非同步、併發/並行

    併發不一定等於並行

 (三)非同步/同步任務 & 並行/串列隊列的特點

  

     綜上所述,iOS多線程編程使用GCD的最優原則是能不在阻礙主線程(又叫作UI線程)的情況下,開啟新的線程(子線程)去處理耗時的操作,以便有效提高程式的執行效率和資源利用率,但是同時開啟多個子線程也會引發許多其他的問題,如資源競爭、死鎖、記憶體損耗等,所以要註意,這篇文章只是介紹GCD的使用,因此可能產生的問題我將會在這個系列後續篇章做介紹。

     GCD併發編程產生的作用歸納(考慮線程安全,不死鎖的情況下效果):

  • 能開啟新的線程(子線程)

  • 多個任務可以同時進行

  • 不會阻塞主線程(又叫作UI線程)影響UI事件

 

三、GCD如何使用

    開發者要做的只是定義想執行的任務並追加到適當的隊列(Dispatch Queue)中   

    1、創建隊列(Dispatch Queue)

    第一種:通過GCD的API的dispatch_queue_create函數生成Dispatch Queue

// 創建串列隊列
dispatch_queue_t queue= dispatch_queue_create("com.beckwang.queue", DISPATCH_QUEUE_SERIAL);

// 創建併發隊列
dispatch_queue_t queue= dispatch_queue_create("com.beckwang.queue", DISPATCH_QUEUE_CONCURRENT);

 

    另外需要註意的點是:雖然有ARC編譯器自動管理記憶體這一優秀技術,但生成的Dispatch Queue必須由程式員主動釋放。

// 釋放
dispatch_release(exampleSerialDispatchQueue) 

// 持有
dispatch_retain(exampleSerialDispatchQueue) 

 

    第二種:直接使用系統提供的標準Dispatch Queue :Main Dispatch Queue和Global Dispatch Queue

  (1)Main Dispatch Queue:主線程中執行的Dispatch Queue,也就是Serial Dispatch Queue(串列隊列),可以通過dispatch_get_main_queue()來獲取。       

dispatch_queue_t   mainDispatchQueue = dispath_get_main_queue();

    (2)  Global Dispatch Queue: 全局併發隊列(Concurrent Dispatch Queue),GCD預設提供了全局的併發隊列,可以通過dispatch_get_global_queue()獲取。

// 高優先順序
dispatch_queue_t globalDispatchQueueHigh = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0)  

// 預設優先順序
dispatch_queue_t globalDispatchQueueDefault = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0) 

// 低優先順序
dispatch_queue_t globalDispatchQueueLow = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0)  

// 後臺優先順序
dispatch_queue_t globalDispatchQueueBackgroud = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_GACKGROUND,0)  

 

    2、創建任務

// 同步執行任務創建方法
dispatch_sync(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);    // 這裡放任務代碼
});

// 非同步執行任務創建方法
dispatch_async(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);    // 這裡放任務代碼
});

 

四、隊列任務組合 

根據(二)中描述,GCD由隊列和任務兩部分組成,隊列分為串列隊列、並行隊列、主隊列,任務可分為同步和非同步任務,這樣可將隊列與任務組合如下:

 

1、並行隊列 & 非同步執行

- (void) asyncConcurrentTask
{
    NSLog(@"asyncConcurrentTask---start");

    dispatch_queue_t queue= dispatch_queue_create("com.beckwang.queue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
        NSLog(@"Task1------%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"Task2------%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"Task3------%@",[NSThread currentThread]);
    });

    NSLog(@"asyncConcurrentTask---end");
}

 列印結果:

2017-07-02 11:13:10.963 Test[6266:2853210] asyncConcurrentTask---start
2017-07-02 11:13:10.963 Test[6266:2853210] asyncConcurrentTask---end
2017-07-02 11:13:10.963 Test[6266:2854044] Task3------<NSThread: 0x60800007cdc0>{number = 5, name = (null)}
2017-07-02 11:13:10.963 Test[6266:2854059] Task2------<NSThread: 0x60800007d1c0>{number = 4, name = (null)}
2017-07-02 11:13:10.963 Test[6266:2854041] Task1------<NSThread: 0x600000074e80>{number = 3, name = (null)}

  結論:

   (1) 開啟了新線程

   (2) 任務之間不需要排隊,且具有同時被執行的權利

 

2、並行隊列 & 同步執行

- (void)syncConcurrentTask
{
    dispatch_queue_t queue = dispatch_queue_create("com.beck.wang.queue", DISPATCH_QUEUE_CONCURRENT);
 
   NSLog(@"syncConcurrentTask---start---");
    
   dispatch_sync(queue, ^{
        NSLog(@"Task1---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"Task2---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"Task3---%@", [NSThread currentThread]);
    });

    NSLog(@"syncConcurrentTask---end---");
}

 列印結果:

2017-07-02 11:25:04.725 Test[6385:2940867] syncConcurrentTask---start---
2017-07-02 11:25:04.725 Test[6385:2940867] Task1---<NSThread: 0x608000067540>{number = 1, name = main}
2017-07-02 11:25:04.726 Test[6385:2940867] Task2---<NSThread: 0x608000067540>{number = 1, name = main}
2017-07-02 11:25:04.726 Test[6385:2940867] Task3---<NSThread: 0x608000067540>{number = 1, name = main}
2017-07-02 11:25:04.726 Test[6385:2940867] syncConcurrentTask---end---

  結論:

   (1) 不開啟了新線程

   (2) 任務之間需要排隊,按照追加順序執行

 

3、串列隊列 & 非同步執行

- (void)asyncSerialTask
{
    dispatch_queue_t queue = dispatch_queue_create("com.beckwang.queue", DISPATCH_QUEUE_SERIAL);
 
    NSLog(@"asyncSerialTask---start---");

    dispatch_async(queue, ^{
        NSLog(@"Task1---%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"Task2---%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"Task3---%@", [NSThread currentThread]);
    });
    NSLog(@"asyncSerialTask---end---");
}

 列印結果:

2017-07-02 11:36:27.068 Test[6557:3008079] asyncSerialTask---start---
2017-07-02 11:36:27.068 Test[6557:3008079] asyncSerialTask---end---
2017-07-02 11:36:27.068 Test[6557:3008342] Task1---<NSThread: 0x600000071e00>{number = 3, name = (null)}
2017-07-02 11:36:27.069 Test[6557:3008342] Task2---<NSThread: 0x600000071e00>{number = 3, name = (null)}
2017-07-02 11:36:27.069 Test[6557:3008342] Task3---<NSThread: 0x600000071e00>{number = 3, name = (null)}

 結論:

   (1) 開啟了新線程

   (2) 任務之間需要排隊,按照追加順序執行

 

4、串列隊列 & 同步執行

- (void)syncSerialTask
{
    dispatch_queue_t queue = dispatch_queue_create("com.beckwang.queue", DISPATCH_QUEUE_SERIAL);
 
    NSLog(@"syncSerialTask---start---");

    dispatch_sync(queue, ^{
        NSLog(@"Task1---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"Task2---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"Task3---%@", [NSThread currentThread]);
    });

    NSLog(@"syncSerialTask---end---");
}

 列印結果:

2017-07-02 13:17:48.948 Test[7238:3192943] syncSerialTask---start---
2017-07-02 13:17:48.948 Test[7238:3192943] Task1---<NSThread: 0x600000076640>{number = 1, name = main}
2017-07-02 13:17:48.949 Test[7238:3192943] Task2---<NSThread: 0x600000076640>{number = 1, name = main}
2017-07-02 13:17:48.949 Test[7238:3192943] Task3---<NSThread: 0x600000076640>{number = 1, name = main}
2017-07-02 13:17:48.949 Test[7238:3192943] syncSerialTask---end---

結論:

   (1) 不開啟了新線程

   (2) 任務之間需要排隊,按照追加順序執行

 

5、主隊列 & 非同步執行

- (void)asyncMainTask
{
    dispatch_queue_t queue = dispatch_get_main_queue();
 
    NSLog(@"asyncMainTask---start---");

    dispatch_async(queue, ^{
        NSLog(@"Task1---%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"Task2---%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"Task3---%@", [NSThread currentThread]);
    });

    NSLog(@"asyncMainTask---end---");
}

列印結果:

2017-07-02 13:19:36.828 Test[7272:3206224] asyncMainTask---start---
2017-07-02 13:19:36.828 Test[7272:3206224] asyncMainTask---end---
2017-07-02 13:19:36.834 Test[7272:3206224] Task1---<NSThread: 0x608000072480>{number = 1, name = main}
2017-07-02 13:19:36.834 Test[7272:3206224] Task2---<NSThread: 0x608000072480>{number = 1, name = main}
2017-07-02 13:19:36.834 Test[7272:3206224] Task3---<NSThread: 0x608000072480>{number = 1, name = main}

 結論:

   (1) 不開啟了新線程

   (2) 任務之間需要排隊,按照追加順序執行

 

6、主隊列 & 同步執行

- (void)syncMainTask{

    dispatch_queue_t queue = dispatch_get_main_queue();
 
    NSLog(@"syncMainTask---start---");

    dispatch_sync(queue, ^{
        NSLog(@"Task1---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"Task2---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"Task3---%@", [NSThread currentThread]);
    });

    NSLog(@"syncMainTask---end---");
}

 列印結果:

2017-07-02 13:22:31.860 Test[7335:3230988] syncMainTask---start---

結論:

發生死鎖,程式崩潰。

 

好了GCD系列的上篇就寫到這裡,我將在後續系列中詳細介紹GCD的隊列系列和用法,以及使用GCD可能造成的問題及解決方案,水平有限,有不對的地方還望批評指正!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在字面量方式中,我們//之間包起來的所有的內容都是元字元,有的具有特殊意義,大部分都是代表本身含義的普通的元字元 為瞭解決上述想在正則裡面加上一個變數這樣的需求,我們只能使用實例創建的方式了 var reg = new RegExp("^\\d+"+name+"\\d+$","g") 字面量方式和實 ...
  • function busUpLoadImg(postUrl,id) { .......//省略部分不用修改 uploader.on('uploadSuccess', function(file) { $('#' + file.id).addClass('upload-state-done'); //... ...
  • 手機號碼: 電子郵箱: 身份證: 銀行卡: ...
  • 廢話不多說,直接進入主題,margin相關技巧。 1、設置元素水平居中:margin:x auto; 2、margin負值讓元素位移及邊框合併。 外邊距合併 指當兩個垂直外邊距相遇時,它們將形成一個外邊距。合併後的外邊距的高度等於兩個發生合併的外邊距的高度中的較大者。 解決外邊距合併的方法: a、使 ...
  • 這周末在家呆了兩天,正好中午閑暇時間繼續分享Angularjs相關,今天主要分享Angularjs總體介紹及數據綁定部分內容,下麵直接進入主題。 1、基本概念: AngularJS是為了剋服HTML在構建應用上的不足而設計的。HTML是一門很好的偽靜態文本展示設計的聲明式語言,但要構建WEB應用的話 ...
  • 需求:最近公司需要做一個樓宇對講的功能:門口機(連接WIFI)撥號對室內機(對應的WIFI)的設備進行呼叫,室內機收到呼叫之後將對收到的數據進行UDP廣播的轉發,手機(連接對應的WIFI)收到視頻流之後,實時的展示視頻數據(手機可以接聽,掛斷,手機接聽之後,室內機不展示視頻,只是進行轉發。) 簡單點 ...
  • 原作者,介紹Kotlin中密封類。這些新概念讓您更接近函數式編程成為可能。 ...
  • 把我認為最好的知識,拿來與他人分享,是這一生快事之一! React Native 項目常用第三方組件彙總: react-native-animatable 動畫 react-native-carousel 輪播 react-native-countdown 倒計時 react-native-devi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...