《OC高級編程》之 GCD

GCD概要

?

多線程編程

? ? 一個 CPU 一次只能執(zhí)行一個命令,一個 CPU 執(zhí)行的 CPU 命令列為一條無分叉路經(jīng),當(dāng)這種路徑存在多條時,即為“多線程”,使用多線程的程序可以在某個線程和其他線程之間反復(fù)多次進(jìn)行上下文切換,就好像能并列執(zhí)行多線程一樣,在具有多 CPU 核的情況下,就是真正并行執(zhí)行了

? ? 容易產(chǎn)生的問題:數(shù)據(jù)競爭,死鎖,消耗大量內(nèi)存
?

GCD 的 API

?

Dispatch Queue

dispatch_async(queue, ^{
    //想執(zhí)行的任務(wù)
})

? ? Dispatch Queue 有兩種:Serial Dispatch Queue 和 Concurrent Dispatch Queue,前者使用一個線程,后者使用多個線程,在不能改變執(zhí)行的處理順序或不想并行執(zhí)行多個處理時使用 Serial Dispatch Queue
?

dispatch_queue_create

? ? 通過 GCD 的 API 生成 Dispatch Queue

? ? 雖然 Serial Dispatch Queue 同時只能追加一個處理,但可用函數(shù)生成任意多個Dispatch Queue,多個 Serial Dispatch Queue 可并行執(zhí)行(可能造成內(nèi)存消耗問題),只在為了避免數(shù)據(jù)競爭時使用 Serial Dispatch Queue

? ? 當(dāng)想并行執(zhí)行不發(fā)生數(shù)據(jù)競爭等問題的處理時,使用 Concurrent Dispatch Queue,不管生成多少,XUN 內(nèi)核只使用有效管理的內(nèi)存

//生成Serial Dispatch Queue 第一個參數(shù)為名字
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
//生成Concurrent Dispatch Queue
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
//執(zhí)行
dispatch_async(myConcurrentDispatchQueue, ^{NSLog(@"haha");});

? ? 通過 dispatch_queue_create 生成的 Dispatch Queue 在使用結(jié)束后通過dispatch_release 釋放

? ? 在 dispatch_async 函數(shù)中追加 Block 到 Dispatch Queue,并立即釋放完全沒有問題,因為 Block 持有了 Dispatch Queue,在 Block 執(zhí)行結(jié)束后被釋放
?

Main Dispatch Queue/Global Dispatch Queue

? ? 可以直接獲取系統(tǒng)標(biāo)準(zhǔn)提供的 Dispatch Queue,前者是在主線程執(zhí)行的 Serial Dispatch Queue,后者是 Concurrent Dispatch Queue

? ? Global Dispatch Queue 有四個執(zhí)行優(yōu)先級:High Priority/Default Priority/Low Priority/Background Priority

? ? 對 Main Dispatch Queue 做 retain 和 release 不會起任何變化,也不會有問題

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            /*
             * 可并行執(zhí)行的處理
             */
            
            /*
             * 在Main Dispatch Queue中執(zhí)行Block
             */
            dispatch_async(dispatch_get_main_queue(), ^{
                /*
                 * 只能在主線程中執(zhí)行的處理
                 */
            });
        }); 

?

dispatch_set_target_queue

? ? dispatch_queue_create 生成的 Dispatch Queue 都使用與默認(rèn)優(yōu)先級 Global Dispatch Queue 相同執(zhí)行優(yōu)先級的線程,而變更執(zhí)行優(yōu)先級要使用dispatch_set_target_queue

dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);

?? ? 第一個參數(shù)為指定要變更執(zhí)行優(yōu)先級的 Dispatch Queue,不可為 Main/Global Dispatch Queue

? ? 不僅可以變更執(zhí)行優(yōu)先級,還可以作為 Dispatch Queue 的執(zhí)行階層,如果在多個 Serial Dispatch Queue 中指定某一個,那么原本應(yīng)并行執(zhí)行的多個 Serial Dispatch Queue,在目標(biāo) Queue 上只能同時執(zhí)行一個
?

dispatch_after

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"waited at least three secs");
});

? ? dispatch_after 在指定時間后追加處理到 Dispatch Queue(<font color="tomato">不是執(zhí)行處理</font>)

? ? ull 是 C 語言的數(shù)值字面量,是顯式表明類型時使用的字符串(表示“unsigned long long”)

? ? dispatch_time_t 類型可使用 dispatch_time 或 dispatch_walltime 函數(shù)作成,dispatch_time 通常用于計算相對時間,dispatch_walltime 用于計算絕對時間
?

Dispatch Group

? ? 在追加到 Dispatch Queue 中的多個處理全部結(jié)束后想執(zhí)行結(jié)束處理,只使用一個 Serial Dispatch Queue 時,可將結(jié)束處理追加到最后,但多個時使用 Group

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{NSLog(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"done");});

//blk1
//blk2
//blk0
//done

? ? 追加處理的執(zhí)行順序不定,但執(zhí)行結(jié)果的 done 一定是最后輸出的。

? ? 無論向什么樣的 Dispatch Queue 追加處理,使用 Dispatch Group 都可監(jiān)視這些處理執(zhí)行的結(jié)束,一旦結(jié)束,就將結(jié)束的處理追加到 Dispatch Queue 中(不一定是同一個 Queue)

? ? 也可以使用 dispatch_group_wait 函數(shù)

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

? ? 第二個參數(shù)為等待時間,DISPATCH_TIME_FOREVER 意味著永久等待,如果 dispatch_group_wait 函數(shù)的返回值不為 0,意味著經(jīng)過指定時間,Dispatch Group 的某一個處理還在執(zhí)行,為 0 則全部執(zhí)行結(jié)束,DISPATCH_TIME_FOREVER 的返回值恒為 0

? ? 等待意味著一旦調(diào)用 dispatch_group_wait,該函數(shù)就處于調(diào)用的狀態(tài)而不返回,即執(zhí)行該函數(shù)的線程停止
?

dispatch_barrier_async

? ? 該函數(shù)和 Concurrent Dispatch Queue 一起使用,dispatch_barrier_async 會等待追加到 Concurrent Dispatch Queue 上的并行執(zhí)行的處理全部結(jié)束后,再將指定的處理追加到該 Concurrent Dispatch Queue 中,然后在該處理執(zhí)行完畢后,Concurrent Dispatch Queue 才恢復(fù)為一般的動作,又開始并行執(zhí)行

dispatch_barrier_sync(queue, blk_for_writing);

? ? 使用該函數(shù)可實現(xiàn)高效率的數(shù)據(jù)庫訪問和文件訪問
?

dispatch_sync

? ? 意味著將 Block “同步”到指定的 Dispatch Queue 中,在追加 Block 結(jié)束之前,dispatch_sync 函數(shù)會一直等待,即當(dāng)前線程停止

易造成死鎖

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{NSLog(@"Hello");});
//Serial Dispatch Queue也會引起同樣問題
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_async(queue, ^{
    dispatch_sync(queue, ^{NSLog(@"Hello");});
});

?

dispatch_apply

? ? dispatch_apply 函數(shù)是 dispatch_sync 函數(shù)和 Dispatch Group 的關(guān)聯(lián) API。該函數(shù)按指定的次數(shù)將特定的 Block 追加到指定的 Dispatch Queue 中,并等待全部處理執(zhí)行結(jié)束

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"%zu", index);
});
NSLog(@"done");

? ? 各個處理的執(zhí)行時間不定,但輸出結(jié)果的 done 必定在最后的位置

? ? 推薦在 dispatch_async 函數(shù)中非同步地執(zhí)行 dispatch_apply 函數(shù)

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
    dispatch_apply([array count], queue, ^(size_t index) {
        NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
    });
            
    dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"done");
    });
});

?

dispatch_suspend/dispatch_resume

? ? 希望不執(zhí)行已追加的處理時可以掛起 Dispatch Queue

//掛起
dispatch_suspend(queue);
//恢復(fù)
dispatch_resume(queue);

?

Dispatch Semaphore

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
        
for(int i = 0; i < 100000; ++i) {
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        [array addObject: [NSNumber numberWithInt: i]];
        dispatch_semaphore_signal(semaphore);
    });
}
        
dispatch_release(semaphore);

?

dispatch_once

?? ? 保證程序只執(zhí)行一次處理,如進(jìn)行初始化的代碼可以通過 dispatch_once 簡化

static int initialized = NO;
if(initialized == NO){
    //初始化
    initialized = YES;
}
//簡化
static dispatch_once_t pred;
dispatch_once(&pred, ^{
    //初始化
})

? ? 通過 dispatch_once 函數(shù),即使在多線程環(huán)境下執(zhí)行也可保證安全。這就是所說的單例模式,在生成單例對象時使用

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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