原創(chuàng)文章轉(zhuǎn)載請注明出處柱徙,謝謝
重溫了一遍關(guān)于GCD方面的一些知識,于是重新整理了一下奇昙。
關(guān)于Dispatch Queue
Dispatch Queue可以分為兩種隊列护侮,一種是等待現(xiàn)在執(zhí)行中處理的Serial Dispatch Queue,即串行執(zhí)行储耐;另一種是不等待現(xiàn)在執(zhí)行中處理的Concurrent Dispatch Queue圣蝎,即并行執(zhí)行酸役;
Concurrent Dispatch Queue并行執(zhí)行的處理數(shù)是由CPU核數(shù)重贺,CPU負(fù)荷以及Dispatch Queue中的處理所決定几晤。
關(guān)于dispatch-queue-create
dispatch-queue-create用于創(chuàng)建Dispatch Queue,可以創(chuàng)建Serial Dispatch Queue和Concurrent Dispatch Queue兩種禽炬。
在《Objective-C高級編程iOS與OSX多線程和內(nèi)存管理》一書中說到dispatch-queue-create不受ARC控制,需要我們自己手動disaptch-retain和dispatch-release勤家,這個其實是不對的腹尖。在官方的文檔中已經(jīng)說明在OSX10.8和iOS10.6以后,ARC已經(jīng)支持自動管理Dispatch Queue的創(chuàng)建了伐脖,不要我們手動release和retain了热幔;但是如果你需要在開啟ARC的情況下同時手動retian/release,那么就需要在compiler flags設(shè)置-DOS-OBJECT-USE-OBJC = 0讼庇。
關(guān)于Main Disaptch Queue和Global Disaptch Queue
Main Disaptch Queue和Global Disaptch Queue是兩個系統(tǒng)的標(biāo)準(zhǔn)Dispatch Queue绎巨。
Main Disaptch Queue是在主線程中執(zhí)行的Dispatch Queue,因為主線程只有一個蠕啄,所以Main Dispatch Queue就是Serial Dispatch Queue场勤。
Global Disaptch Queue是所有線程都可以使用的Concurrent Dispatch Queue,Global Disaptch Queue有四個執(zhí)行優(yōu)先級:
- High Priority(最高優(yōu)先級)
- Default Priority(默認(rèn)優(yōu)先級)
- Low Priority(低優(yōu)先級)
- Background Priority(后臺優(yōu)先級)
但是通過XNU內(nèi)核用于Global Disaptch Queue的線程并不能保持實時性歼跟,因此執(zhí)行優(yōu)先級只是大致的判斷和媳。Global Disaptch Queue的默認(rèn)執(zhí)行優(yōu)先級是Default Priority。
/*
* Main Dispatch Queue
*/
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
/*
* Global Dispatch Queue(High Priority)
*/
dispatch_queue_t globalDispatchQueueHigh =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
/*
* Global Dispatch Queue(Default Priority)
*/
dispatch_queue_t globalDispatchQueueDefault =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*
* Global Dispatch Queue(Low Priority)
*/
dispatch_queue_t globalDispatchQueueLow =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
/*
* Global Dispatch Queue(Background Priority)
*/
dispatch_queue_t globalDispatchQueueBackground =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
關(guān)于dispatch-set-target-queue
dispatch-set-target-queue一共有兩個作用哈街。
作用一:修改Dispatch Queue的執(zhí)行優(yōu)先級留瞳;通過dispatch-queue-create函數(shù)生成的Dispatch Queue默認(rèn)優(yōu)先級都是Default Priority。
/*
*修改serialQueue優(yōu)先級至Background Priority
*/
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.serialQueue", NULL);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(serialQueue, globalQueue);
注意不要修改Main Disaptch Queue和Global Disaptch Queue的優(yōu)先級骚秦,因為這種情況是不可預(yù)知的她倘。
作用二:修改用戶隊列的目標(biāo)隊列璧微,使多個Serial Dispatch Queue在目標(biāo)Queue只能同時執(zhí)行一個處理,防止并行執(zhí)行硬梁。
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("com.example.gcd.queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("com.example.gcd.queue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("com.example.gcd.queue3", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queue1, serialQueue);
dispatch_set_target_queue(queue2, serialQueue);
dispatch_set_target_queue(queue3, serialQueue);
dispatch_async(queue1, ^{
NSLog(@"queue1-start");
sleep(1.0f);
NSLog(@"queue1-end");
});
dispatch_async(queue2, ^{
NSLog(@"queue2-start");
sleep(1.0f);
NSLog(@"queue2-end");
});
dispatch_async(queue3, ^{
NSLog(@"queue3-start");
sleep(1.0f);
NSLog(@"queue3-end");
});
//out put 多個Serial Queue并發(fā)執(zhí)行前硫,每次只能執(zhí)行一個serial Queue的內(nèi)容
queue1-start
queue1-end
queue2-start
queue2-end
queue3-start
queue3-end
關(guān)于dispatch-after和dispatch-once
dispatch-after函數(shù)并不是在指定時間后執(zhí)行處理,而是在指定時間追加處理時間到Dispatch queue后再進(jìn)行執(zhí)行靶溜。
關(guān)于dispatch-time-t的類型可以由dispatch-time和dispatch-walltime兩個函數(shù)來生成开瞭。
dispatch-time函數(shù)能夠獲取從第一個參數(shù)dispatch-time-t類型值中指定時間開始,到第二個參數(shù)指定的毫微秒單位時間后的時間罩息。此時間是指相對時間嗤详。
// 延時一秒以后
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
dispatch-walltime函數(shù)能夠獲取從第一個參數(shù)struct timespec結(jié)構(gòu)體時間開始,此時間是指絕對時間瓷炮。
struct timespec類型的時間可以通過NSDate類對象轉(zhuǎn)換而成葱色。
NSDate *date = [NSDate date];
NSTimeInterval interval;
double second, subsecond;
struct timespec time;
interval = [date timeIntervalSince1970];
subsecond = modf(interval, &second);
time.tv_sec = second;
time.tv_nsec = subsecond * NSEC_PER_SEC;
dispatch_time_t milestone milestone = dispatch_walltime(&time, 0);
dispatch-once函數(shù)的目的是保證在應(yīng)用程序中執(zhí)行中只執(zhí)行指定處理,經(jīng)常出現(xiàn)在單例的初始化里面娘香,通過disptach-once函數(shù)苍狰,即使在多線程環(huán)境下執(zhí)行也是安全的。
static dispatch_once_t once;
dispatch_once(&once, ^{
// init class
});
關(guān)于Dispatch Group的作用
Dispatch Group的作用是當(dāng)隊列中的所有任務(wù)都執(zhí)行完畢后在去做一些操作烘绽,主要針對Concurrent Dispatch Queue中多個處理結(jié)束后追加的操作淋昭。
Dispatch Group分為兩種方式可以實現(xiàn)上面需求。
第一種是使用dispatch-group-async函數(shù)安接,將隊列與任務(wù)組進(jìn)行關(guān)聯(lián)并自動執(zhí)行隊列中的任務(wù)翔忽。
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(@"blk1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk3");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"done");
});
// output
blk3
blk2
blk1
done
dispatch-group-async函數(shù)會將隊列與相應(yīng)的任務(wù)組進(jìn)行關(guān)聯(lián)同時自動執(zhí)行,當(dāng)與任務(wù)組關(guān)聯(lián)的隊列中的任務(wù)都執(zhí)行完畢后盏檐,會通過dispatch-group-notify函數(shù)發(fā)出通知告訴用戶任務(wù)組中的所有任務(wù)都執(zhí)行完畢了歇式,有點類似于dispatch-barrier-async,另外dispatch-group-notify方式并不會阻塞線程胡野。
但是如果我們使用dispatch-group-wait函數(shù)材失,那么就會阻塞當(dāng)前線程,等待全部處理執(zhí)行結(jié)束硫豆。
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(@"blk1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk3");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"done");
//output
blk2
blk3
blk1
done
dispatch-group-wait函數(shù)的返回值不為0就意味著雖然經(jīng)過了指定的時間龙巨,但是屬于Dispatch Group的某一個處理還在執(zhí)行中,如果返回值為0為全部執(zhí)行結(jié)束熊响,當(dāng)?shù)却龝r間為DISPATCH-TIME-FOREVER恭应,由dispatch-group-wait函數(shù)返回時,由于屬于Dispatch Group的處理必定全部執(zhí)行結(jié)束耘眨,因此返回值一直為0昼榛。當(dāng)指定為DISPATCH-TIME-NOW則不用任何等待即可判定屬于Dispatch Group的處理是否執(zhí)行結(jié)束。
第二種是使用手動的將隊列與組進(jìn)行關(guān)聯(lián)然后使用異步將隊列進(jìn)行執(zhí)行,也就是dispatch-group-enter與dispatch-group-leave方法的使用胆屿。dispatch-group-enter函數(shù)進(jìn)入到任務(wù)組中奥喻,然后異步執(zhí)行隊列中的任務(wù),最后使用dispatch-group-leave函數(shù)離開任務(wù)組即可非迹。
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"blk1");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"blk2");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"blk3");
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"done");
});
// output
blk2
blk3
blk1
done
dispatch-group-enter和dispatch-group-leave必須同時匹配出現(xiàn)才可以环鲤,不然就會出現(xiàn)無法預(yù)知的情況。
關(guān)于dispatch-barrier-async
dispatch-barrier-async函數(shù)的目的基本就是為了讀寫鎖的問題憎兽。
對于Concurrent Dispatch Queue可能會所產(chǎn)生的數(shù)據(jù)庫同時讀寫的問題冷离,使用dispatch-barrier-async就可以很好的避免這個問題。
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.queue", DISPATCH_QUEUE_CONCURRENT);
__block NSInteger index = 0;
dispatch_async(queue, ^{NSLog(@"blk0_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk1_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk2_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk3_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk4_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk5_for_reading index %ld", index);});
dispatch_barrier_sync(queue, ^{
NSLog(@"----------------------------------");
index++;
});
dispatch_async(queue, ^{NSLog(@"blk6_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk7_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk8_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk9_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk10_for_reading index %ld", index);});
dispatch_async(queue, ^{NSLog(@"blk11_for_reading index %ld", index);});
//output
blk0_for_reading index 0
blk1_for_reading index 0
blk2_for_reading index 0
blk3_for_reading index 0
blk5_for_reading index 0
blk4_for_reading index 0
----------------------------------
blk6_for_reading index 1
blk7_for_reading index 1
blk8_for_reading index 1
blk9_for_reading index 1
blk10_for_reading index 1
blk11_for_reading index 1
dispatch-barrier-sync 函數(shù)會等待追加到Concurrent Dispatch Queue上的并行執(zhí)行的處理全部結(jié)束以后纯命,再將指定的處理追加到該Concurrent Dispatch Queue中西剥。然后在由Concurrent Dispatch Queue函數(shù)追加的處理執(zhí)行完畢后。Concurrent Dispatch Queue才恢復(fù)為一般的動作亿汞,追加到該Concurrent Dispatch Queue的處理又開始并行執(zhí)行瞭空。
關(guān)于dispatch-suspend(掛起)/dispatch-resume(恢復(fù))
dispatch-suspend/dispatch-resume一般用于當(dāng)追加大量處理到Dispatch Queue時,在追加處理的過程中有時希望不執(zhí)行已追加的處理疗我。
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_suspend(queue);
dispatch_async(queue, ^{
NSLog(@"blk0");
});
dispatch_async(queue, ^{
NSLog(@"blk1");
});
sleep(2);
dispatch_resume(queue);
// 兩秒后恢復(fù)queue咆畏,然后才會執(zhí)行queue里面的操作
關(guān)于dispatch-sync
dispatch-sync相對于dispatch-async的區(qū)別就在于它是就是同步的線程操作,只有指定的block完成以后dispatch-sync才會返回吴裤。
但是dispatch-sync會帶來一些死鎖的情況旧找。
將主線程的Main Disaptch Queue,在主線程中執(zhí)行dispatch_sync就會造成死鎖麦牺。
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"lock");
});
因為主線程正在執(zhí)行上述代碼塊钦讳,所以此時的block無法執(zhí)行到Main Disaptch Queue,由于Main Disaptch Queue是Serial Dispatch Queue;但是由于block無法執(zhí)行枕面,所以dispatch-sync就會一直等待block的執(zhí)行,主線程此時死鎖缚去。
下面的例子也是同樣的道理潮秘。
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"lock");
});
});
所以由此可知,我把Main Disaptch Queue替換成任何Serial Dispatch Queue都會造成死鎖的問題易结。
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.queue", NULL);
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"lock");
});
});
關(guān)于Dispatch Semaphore的問題
Dispatch Semaphore主要用于信號量的控制并發(fā)枕荞,當(dāng)處理一系列線程的時候,當(dāng)數(shù)量達(dá)到一定量時搞动,我們需要控制線程的并發(fā)量躏精。
在GCD中有三個函數(shù)是semaphore的操作,分別是:
- dispatch-semaphore-create 創(chuàng)建一個初始指定數(shù)量的信號量
- dispatch-semaphore-signal 發(fā)送一個信號鹦肿,使信號量+1矗烛,計數(shù)為0時等待,計數(shù)為1或大于1時箩溃,減去1而不等待瞭吃。
- dispatch-semaphore-wait 等待信號碌嘀,使信號量-1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSInteger index = 0; index < 50; index++) {
/**
*等待處理,直到信號量>0歪架,信號量減1股冗,當(dāng)信號量為0時不需要在減1
*/
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(global, ^{
NSLog(@"thread %ld", index);
/**
*數(shù)據(jù)處理完畢,信號量加1
*/
dispatch_semaphore_signal(semaphore);
});
}
總結(jié)
GCD算是OC中一項很基礎(chǔ)的知識了和蚪,靈活使用GCD會很大程度上提高我們的代碼質(zhì)量止状。