GCD

GCD的簡單易用,很容易愛不釋手。今天趁機梳理一下GCD相關(guān)知識。

線程、任務(wù)和隊列

首先來認識一下隊列和任務(wù)
引自:行走的少年郎

任務(wù):就是執(zhí)行操作的意思,換句話說就是你在線程中執(zhí)行的那段代碼。在GCD中是放在block中的。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行異步執(zhí)行。兩者的主要區(qū)別是:是否具備開啟新線程的能力。

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

隊列:隊列用來管理線程,也就是線程池,控制線程的創(chuàng)建和執(zhí)行。這里的隊列指任務(wù)隊列,即用來存放任務(wù)的隊列。隊列是一種特殊的線性表,采用FIFO(先進先出)的原則,即新任務(wù)總是被插入到隊列的末尾,而讀取任務(wù)的時候總是從隊列的頭部開始讀取。每讀取一個任務(wù),則從隊列中釋放一個任務(wù)。在GCD中有兩種隊列:串行隊列并發(fā)隊列。

  • 并發(fā)隊列(Concurrent Dispatch Queue):可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
  • 串行隊列(Serial Dispatch Queue):讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù))。

線程:可并發(fā)執(zhí)行的,擁有最小系統(tǒng)資源,共享進程資源的基本調(diào)度單位。
共用堆,自有棧(官方資料說明iOS主線程棧大小為1M,其它線程為512K)。
并發(fā)執(zhí)行進度不可控,對非原子操作易造成狀態(tài)不一致,加鎖控制又有死鎖的風(fēng)險。

何為GCD

百度一下
Grand Central Dispatch (GCD)是Apple開發(fā)的一個多核編程的較新的解決方法。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)。它是一個在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。

優(yōu)點
  • 讓程序平行的排列各個任務(wù),根據(jù)當(dāng)前的系統(tǒng)資源來合理的安排和分配任務(wù)
  • 能充分利用多核CUP資源
  • 自動管理線程的生命周期,程序員脫離了手動管理線程的苦海

使用

1、創(chuàng)建隊列
  • 串行隊列:
    使用dispatch_queue_create(const char * label, dispatch_queue_attr_t attr),label 參數(shù)表示此隊列的標識符,調(diào)試的時候可以用到,第二個參數(shù)attr是類型標識符,創(chuàng)建串行隊列時使用DISPATCH_QUEUE_SERIAL,創(chuàng)建并發(fā)隊列時使用DISPATCH_QUEUE_CONCURRENT
//創(chuàng)建一個串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("com.luqiuan", DISPATCH_QUEUE_SERIAL);

程序開始運行時會自己創(chuàng)建一個主線程放在主隊列中,主隊列也是一個串行隊列,主隊列里只存在一個主線程,我們的大部分代碼邏輯都可以在主線程里面處理,如果沒有特殊要求(比如耗時操作)我們一般在不需要創(chuàng)建其他線程來增加程序的復(fù)雜度。可以用dispatch_get_main_queue()來獲取主隊列。

  • 并發(fā)隊列
    并發(fā)隊列的創(chuàng)建與串行相似,只需要改變第二個參數(shù)即可
//創(chuàng)建一個并發(fā)隊列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.luqiuan", DISPATCH_QUEUE_CONCURRENT);

還可以使用dispatch_get_global_queue來創(chuàng)建全局并發(fā)隊列。GCD默認提供了全局的并發(fā)隊列,需要傳入兩個參數(shù)。第一個參數(shù)表示隊列優(yōu)先級,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二個參數(shù)暫時沒用,用0即可。

2、 任務(wù)的創(chuàng)建方法
// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
     NSLog(@"%@",[NSThread currentThread]); 
    // 這里放任務(wù)代碼
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);
    // 這里放任務(wù)代碼
});

雖然使用GCD只需兩步,但是既然我們有兩種隊列,兩種任務(wù)執(zhí)行方式,那么我們就有了四種不同的組合方式。這四種不同的組合方式是
并發(fā)隊列 + 同步執(zhí)行 : 不會創(chuàng)建新線程,任務(wù)加入隊列時會立馬串行執(zhí)行
并發(fā)隊列 + 異步執(zhí)行 : 具備創(chuàng)建新線程的能力,先將任務(wù)加入隊列后再開始并行執(zhí)行
串行隊列 + 同步執(zhí)行 : 不會創(chuàng)建新線程,先將任務(wù)加入隊列后再串行執(zhí)行
串行隊列 + 異步執(zhí)行 : 具備創(chuàng)建新線程的能力,先將任務(wù)加入隊列后再串行執(zhí)行
主隊列 + 同步執(zhí)行 : 不會創(chuàng)建新線程,任務(wù)串行執(zhí)行
主隊列 + 異步執(zhí)行 : 不會創(chuàng)建新線程,任務(wù)串行執(zhí)行

GCD其他功能

1、延時操作
//延時執(zhí)行
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延時兩秒");
    });
2、保證代碼執(zhí)行的唯一性
//保證代碼只執(zhí)行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"這段代碼只執(zhí)行一次");
    });
3、GCD快讀迭代
//GCD快讀迭代,當(dāng)我們用for循環(huán)便利一個數(shù)組時,一般會按順序一個一個便利數(shù)組元素,使用GCD迭代可以提高速率,同時遍歷多個元素
    dispatch_queue_t ser1 = createQueue();
    
    NSArray *arr = @[@"一",@"二",@"三",@"四",@"五",@"六",@"七",@"八",@"九"];
    
    dispatch_apply(9, ser1, ^(size_t index) {
        NSLog(@"%@",arr[index]);
    });
4、GCD的柵欄方法

當(dāng)我們需要等待并發(fā)隊列中兩個異步任務(wù)都完成時,才執(zhí)行某一操作時,可以使用GCD的柵欄dispatch_barrier_async,柵欄就像一個判斷器只有當(dāng)隊列中柵欄前面的所有任務(wù)執(zhí)行完成時,才會執(zhí)行后面的任務(wù)。柵欄只能在用來同一個隊列中,如果是不同隊列中的任務(wù),我們可以只用GCD隊列組,稍后介紹。

NSLog(@"========begin===");
    dispatch_queue_t serQueue = createQueue();
    dispatch_async(serQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"第一個任務(wù)%d",i);
            sleep(4);
        }
    });
    
    dispatch_async(serQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"第二個任務(wù)%d",i);
            sleep(1);
        }
    });
    //用同步的話會阻塞主線程,所以采用異步執(zhí)行
    dispatch_barrier_async(serQueue,^{
        NSLog(@"柵欄柵欄--------------");
    });
    
    dispatch_async(serQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"第三個任務(wù)%d",i);
            sleep(1);
        }
    });
    
    NSLog(@"========end=====");
4、GCD隊列組

上面說的是等待同一隊列中不同的并發(fā)任務(wù)執(zhí)行完,而隊列組我們可以用于等待不同隊列中的任務(wù)執(zhí)行完時成時再執(zhí)行某一操作,直接上代碼:

//GCD隊列組,當(dāng)我們需要等兩個耗時操作完成時再回到指定隊列工作時,我們可以使用GCD隊列組
    dispatch_group_t gcdGroup = dispatch_group_create();
    dispatch_queue_t serQueue = dispatch_queue_create("com.luqiuan", DISPATCH_QUEUE_SERIAL);
    //serQueue隊列中的任務(wù)1
    dispatch_group_async(gcdGroup, serQueue, ^{
        NSLog(@"第一個耗時操作");
        sleep(3);
    });
    //全局隊列中的任務(wù)2
    dispatch_group_async(gcdGroup, dispatch_get_global_queue(0, 0), ^{
        sleep(2);
        NSLog(@"第二個耗時操作");
        sleep(3);
    });
    //任務(wù)1和任務(wù)2都完成時,回到主線程執(zhí)行
    dispatch_group_notify(gcdGroup, dispatch_get_main_queue()   , ^{
        NSLog(@"回到主線程");
    });
5、GCD設(shè)置上下文和清理函數(shù)
//設(shè)置上下文
void dispatch_set_context(dispatch_object_t object, void *context);
//獲取上下文
void *dispatch_get_context(dispatch_object_t object);
//清理函數(shù),當(dāng) queue 的引用計數(shù)到達 0 時執(zhí)行清理函數(shù)
void dispatch_set_finalizer_f(dispatch_object_t object,dispatch_function_t finalizer);

代碼

// 當(dāng) queue 的引用計數(shù)到達 0 時執(zhí)行清理函數(shù)
void finalizerFunction(void *context) {
    char *theData = (char *)context;
    printf("myFinalizerFunction - data = %s\n", theData);
    // 具體清理細節(jié)可以另寫一個函數(shù)
    myCleanUpDataContextFunction(theData);
}

// 具體清理細節(jié)
void myCleanUpDataContextFunction(char *data) {
    printf("myCleanUpDataContextFunction - data = %s\n", data);
}

dispatch_queue_t createQueue(){
    
    char *data = "我是一個字符串";
    dispatch_queue_t serialQueue = dispatch_queue_create("com.luqiuan", DISPATCH_QUEUE_CONCURRENT);
    if (serialQueue) {
        dispatch_set_context(serialQueue, data);
        dispatch_set_finalizer_f(serialQueue, &finalizerFunction);
    }
    return serialQueue;
}

void main(){
    dispatch_queue_t serialQueue = createQueue();
    char *name = dispatch_get_context(serialQueue); 
    NSLog(@"==%@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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