什么是GCD别厘?
Grand Central Dispatch(GCD)是蘋果開發(fā)的一項(xiàng)技術(shù)阳堕,用于提升應(yīng)用在多核處理器上運(yùn)行時(shí)的表現(xiàn),使系統(tǒng)和應(yīng)用更快速震放、高效的運(yùn)行。和NSThread以及NSOperationQueue相比驼修,GCD能更靈活的滿足應(yīng)用需求殿遂,并以更加平衡的方式實(shí)現(xiàn)系統(tǒng)資源調(diào)度。
DISPATCH對(duì)象
Dispatch對(duì)象(dispatch_object_t)是對(duì)多種dispatch類型對(duì)象的統(tǒng)稱乙各,包括dispatch_source_t墨礁、dispatch_queue_t、dispatch_group_t耳峦、dispatch_semaphore_t等等恩静。
在使用GCD時(shí),我們通過(guò)dispatch對(duì)象來(lái)實(shí)現(xiàn)對(duì)內(nèi)存蹲坷、任務(wù)以及任務(wù)隊(duì)列的管理驶乾。
在使用OC編程時(shí),所有的dispatch對(duì)象都可以被當(dāng)作OC對(duì)象來(lái)對(duì)待循签。在ARC環(huán)境下级乐,dispatch對(duì)象將被自動(dòng)的引用和釋放;在MRC環(huán)境下县匠,可以通過(guò)dispatch_retain()和dispatch_release()函數(shù)來(lái)實(shí)現(xiàn)對(duì)dispatch對(duì)象的引用和釋放操作风科。
DISPATCH QUEUE
- Dispatch queue(dispatch_queue_t)對(duì)象表示一個(gè)任務(wù)隊(duì)列。任務(wù)隊(duì)列分為串行隊(duì)列和并行隊(duì)列兩種乞旦。
- 一個(gè)串行dispatch queue會(huì)按照FIFO的順序來(lái)執(zhí)行提交到該隊(duì)列的任務(wù)竟痰,并且同一時(shí)間只能執(zhí)行一個(gè)任務(wù)宝磨。
- 一個(gè)并行dispatch queue可以同時(shí)執(zhí)行多個(gè)任務(wù)婿屹。
- 多個(gè)dispatch queue之間是獨(dú)立的煤篙,且互不影響的執(zhí)行添加到自己隊(duì)列的任務(wù)萤彩。
DISPATCH QUEUE的創(chuàng)建
我們有三種方式獲得一個(gè)dispatch queue對(duì)象:
- 獲取主線程隊(duì)列
調(diào)用dispatch_get_main_queue()可以獲取主線程的任務(wù)隊(duì)列务热。這是一個(gè)串行隊(duì)列殉摔。 - 獲取全局并行隊(duì)列
調(diào)用dispatch_get_global_queue()可以獲取全局并行隊(duì)列焰雕。 - 創(chuàng)建一個(gè)隊(duì)列
我們可以通過(guò)調(diào)用dispatch_queue_create()來(lái)自己創(chuàng)建一個(gè)隊(duì)列浊仆。
dispatch_queue_t queue;
//獲取主線程隊(duì)列
queue = dispatch_get_main_queue();
//獲取全局并行隊(duì)列
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建串行隊(duì)列
queue = dispatch_queue_create("label", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建并行隊(duì)列
queue = dispatch_queue_create("label", DISPATCH_QUEUE_CONCURRENT);
向DISPATCH QUEUE添加任務(wù)
- 向一個(gè)dispatch queue添加任務(wù)分為同步添加和異步添加兩種客峭。被添加的任務(wù)既可以是一個(gè)block,也可以是一個(gè)函數(shù)抡柿。
- 使用dispatch_sync() 和 dispatch_sync_f() 將任務(wù)同步添加到隊(duì)列中舔琅,該函數(shù)直到被添加的任務(wù)執(zhí)行完成后才會(huì)返回。
- 使用dispatch_async() 和 dispatch_async_f() 將任務(wù)異步添加到隊(duì)列中洲劣,該函數(shù)調(diào)用后將會(huì)立即返回备蚓,不會(huì)等待被添加的任務(wù)執(zhí)行完成课蔬。
void function(void *context) {
NSNumber *number = (__bridge_transfer NSNumber *)context;
NSLog(@"number = %@", number);
}
- (void)main {
//獲取隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步添加一個(gè)block
dispatch_sync(queue, ^{
/* code in block... */
});
//同步添加一個(gè)函數(shù)
NSNumber *number100 = @(100);
dispatch_sync_f(queue, (__bridge_retained void *)number100, function);
//異步添加一個(gè)block
dispatch_async(queue, ^{
/* code in block... */
});
//異步添加一個(gè)函數(shù)
NSNumber *number200 = @(200);
dispatch_async_f(queue, (__bridge_retained void *)number200, function);
}
DISPATCH SOURCE
Dispatch source對(duì)象(dispatch_source_t)表示一個(gè)事件源,它可以監(jiān)視一個(gè)特定的事件郊尝,并在事件發(fā)生時(shí)向指定的隊(duì)列添加指定的任務(wù)二跋。
DISPATCH SOURCE的創(chuàng)建
- 使用dispatch_source_create()來(lái)創(chuàng)建一個(gè)dispatch source對(duì)象。
該函數(shù)定義如下:
dispatch_source_t dispatch_source_create(
dispatch_source_type_t type,
uintptr_t handle,
unsigned long mask,
dispatch_queue_t queue);
- 第一個(gè)參數(shù)為要監(jiān)視的事件類型流昏,第二個(gè)參數(shù)和第三個(gè)參數(shù)需要根據(jù)第一個(gè)參數(shù)中的事件類型來(lái)確定扎即,第四個(gè)參數(shù)為事件發(fā)生時(shí)任務(wù)要添加到的隊(duì)列。
//創(chuàng)建dispatch source况凉,事件類型為DISPATCH_SOURCE_TYPE_TIMER
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
//設(shè)定timer的開始時(shí)間谚鄙,間隔和延時(shí)誤差
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC, 0.00 * NSEC_PER_SEC);
//設(shè)定事件發(fā)生時(shí)向隊(duì)列添加的任務(wù)
dispatch_source_set_event_handler(timer, ^{
/* code in block */
});
//開始dispatch source
dispatch_resume(timer);
任務(wù)隊(duì)列的同步
多個(gè)dispatch queue間的同步可以通過(guò)多種方式來(lái)實(shí)現(xiàn)。GCD原生提供了dispatch semaphore刁绒、dispatch group闷营、dispatch barrier等多種方式同步。
DISPATCH SEMAPHONE
- Dispatch semaphore(信號(hào))通過(guò)計(jì)數(shù)的方式實(shí)現(xiàn)多個(gè)線程間的同步膛锭,每個(gè)dispatch semaphore對(duì)象都有一個(gè)計(jì)數(shù)值粮坞。
- 調(diào)用dispatch_semaphore_create() 可以創(chuàng)建一個(gè)semaphore對(duì)象。
- 在一個(gè)線程中調(diào)用dispatch_semaphore_wait()可以使semaphore的計(jì)數(shù)減1初狰。如果semaphore的計(jì)數(shù)在減1后小于0莫杈,那么線程進(jìn)入阻塞,直至semaphore的計(jì)數(shù)大于或等于0為止奢入。
- 調(diào)用dispatch_semaphore_signal()可以使semaphore的計(jì)數(shù)加1筝闹。
//創(chuàng)建一個(gè)dispatch semaphore,參數(shù)為semaphore對(duì)象的初始計(jì)數(shù)值
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
//創(chuàng)建a腥光,b兩個(gè)隊(duì)列
dispatch_queue_t queue_a = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue_b = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
//分別向兩個(gè)隊(duì)列添加任務(wù)
dispatch_async(queue_a, ^{
NSLog(@"1");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//由于semaphore初始計(jì)數(shù)為0关顷,調(diào)用dispatch_semaphore_wait后計(jì)數(shù)為-1,隊(duì)列a進(jìn)入阻塞武福。
NSLog(@"4");
});
dispatch_async(queue_b, ^{
NSLog(@"2");
sleep(3);//隊(duì)列b睡眠3秒议双。
NSLog(@"3");
dispatch_semaphore_signal(semaphore);//使semaphore的計(jì)數(shù)加1,加1后semaphore計(jì)數(shù)為0捉片,隊(duì)列a停止阻塞平痰,開始繼續(xù)執(zhí)行。
});
//代碼運(yùn)行后立即輸出1伍纫、2
//3s后依次輸出3宗雇、4
DISPATCH GROUP
- Dispatch group通過(guò)使用dispatch group對(duì)象來(lái)管理多個(gè)隊(duì)列中的多個(gè)任務(wù)。
- 在創(chuàng)建一個(gè)dispatch group對(duì)象后莹规,可以使用dispatch_group_async() 或者dispatch_group_enter() 將任務(wù)(block或者函數(shù))添加到group赔蒲。
- 使用dispatch_group_wait() 來(lái)等待group中的所有任務(wù)都執(zhí)行完畢。
- 使用dispatch_group_notify() 可以使一個(gè)group中的任務(wù)都執(zhí)行完畢后向指定的隊(duì)列發(fā)送通知。
//創(chuàng)建一個(gè)dispatch group對(duì)象
dispatch_group_t group = dispatch_group_create();
//創(chuàng)建一個(gè)串行隊(duì)列和一個(gè)并行隊(duì)列
dispatch_queue_t queue_a = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue_b = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
//向group添加block任務(wù)舞虱,第二個(gè)參數(shù)為執(zhí)行這個(gè)任務(wù)的隊(duì)列
dispatch_group_async(group, queue_b, ^{
NSLog(@"0");
sleep(2);//睡眠2s
NSLog(@"2");
});
//直接向隊(duì)列b添加一個(gè)block任務(wù)
dispatch_async(queue_b, ^{
NSLog(@"1");
dispatch_group_enter(group);//聲明將以下代碼加入到group中去欢际。
sleep(3);//睡眠3s
NSLog(@"3");
dispatch_group_leave(group);//聲明添加到group的代碼在此結(jié)束。
sleep(2);//睡眠2s
NSLog(@"6");
});
//等待group中的任務(wù)全部完成后砾嫉,在主線程隊(duì)列執(zhí)行block
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"5");
});
dispatch_async(queue_a, ^{
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//等待group中的任務(wù)完成幼苛。
NSLog(@"4");
});
//代碼運(yùn)行后將立即輸出0、1
//在第二秒的時(shí)候輸出2
//在第三秒的時(shí)候輸出3焕刮、4舶沿、5
//在第五秒的時(shí)候輸出6
DISPATCH BARRIER
- Dispatch barrier提供了兩個(gè)方法來(lái)同步或異步的向一個(gè)隊(duì)列中添加任務(wù)。使用dispatch_barrier_async() 和dispatch_barrier_sync() 來(lái)異步和同步的向一個(gè)隊(duì)列中添加任務(wù)配并。
- 根據(jù)dispatch barrier可以將一個(gè)隊(duì)列中的任務(wù)分為三部分:在dispatch barrier之前添加的任務(wù)括荡、dispatch barrier添加的任務(wù)(稱為 barrier block)、在dispatch barrier之后添加的任務(wù)溉旋。
- Barrier block將會(huì)等待所有在barrier block之前被添加到隊(duì)列中的任務(wù)都執(zhí)行完畢后才回執(zhí)行畸冲。
在dispatch barrier之后添加到隊(duì)列中的所有任務(wù),將會(huì)一直等待barrier block執(zhí)行完畢后才會(huì)執(zhí)行观腊。 - Dispatch barrier只對(duì)并行隊(duì)列( DISPATCH_QUEUE_CONCURRENT )有效邑闲。
//創(chuàng)建一個(gè)并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
//向隊(duì)列添加一個(gè)任務(wù)
dispatch_async(queue, ^{
NSLog(@"0");
sleep(3);
NSLog(@"2");
});
//向隊(duì)列添加一個(gè)任務(wù)
dispatch_async(queue, ^{
NSLog(@"1");
});
//使用dispatch barrier向隊(duì)列添加一個(gè)任務(wù)
dispatch_barrier_async(queue, ^{
NSLog(@"3");
sleep(1);
NSLog(@"4");
});
//向隊(duì)列添加一個(gè)任務(wù)
dispatch_async(queue, ^{
NSLog(@"5");
});
//代碼運(yùn)行后將立即輸出0、1
//在第三秒輸出2梧油、3
//在第四秒輸出4苫耸、5