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í)行也可保證安全。這就是所說的單例模式,在生成單例對象時使用