GCD全稱為Grand Central Dispatch,是libdispatch的市場名稱,而libdispatch是Apple的一個庫教寂,其為并發(fā)代碼在iOS和OS X的多核硬件上執(zhí)行提供支持杀狡。確切地說GCD是一套低層級的C API,通過 GCD酪夷,開發(fā)者只需要向隊列中添加一段代碼塊(block或C函數(shù)指針)榴啸,而不需要直接和線程打交道。GCD在后端管理著一個線程池捶索,它不僅決定著你的代碼塊將在哪個線程被執(zhí)行插掂,還根據(jù)可用的系統(tǒng)資源對這些線程進行管理。這樣通過GCD來管理線程腥例,從而解決線程被創(chuàng)建的問題
GCD 核心概念
GCD編程的核心就是dispatch隊列辅甥,dispatch block的執(zhí)行最終都會放進某個隊列中去進行。
1燎竖、GCD中的隊列類型
1璃弄、The main queue(主線程串行隊列): 與主線程功能相同,提交至Main queue的任務(wù)會在主線程中執(zhí)行构回,Main queue 可以通過dispatch_get_main_queue()來獲取夏块。
2、Global queue(全局并發(fā)隊列): 全局并發(fā)隊列由整個進程共享纤掸,有高脐供、中(默認)、低借跪、后臺四個優(yōu)先級別政己。Global queue 可以通過調(diào)用dispatch_get_global_queue函數(shù)來獲取(可以設(shè)置優(yōu)先級)
3掏愁、Custom queue (自定義隊列): 可以為串行歇由,也可以為并發(fā)卵牍。Custom queue 可以通過dispatch_queue_create()來獲取沦泌;
4糊昙、Group queue (隊列組):將多線程進行分組,最大的好處是可獲知所有線程的完成情況谢谦。Group queue 可以通過調(diào)用dispatch_group_create()來獲取释牺,通過dispatch_group_notify,可以直接監(jiān)聽組里所有線程完成情況。
2他宛、GCD相關(guān)概念
1船侧、Dispatch Objects
盡管GCD是純C語言的,但它被組建成面向?qū)ο蟮娘L(fēng)格厅各。GCD對象被稱為dispatch object, 所有的dispatch objects都是OC對象.镜撩,就如其他OC對象一樣,當(dāng)開啟了ARC(automatic reference counting)時,dispatch objects的retain和release都會自動執(zhí)行队塘。而如果是MRC的話袁梗,dispatch objects會使用dispatch_retain和dispatch_release這兩個方法來控制引用計數(shù)。
2憔古、Serial & Concurrent
串行任務(wù)就是每次只有一個任務(wù)被執(zhí)行遮怜,并發(fā)任務(wù)就是在同一時間可以有多個任務(wù)被執(zhí)行。
3鸿市、Synchronous & Asynchronous
同步函數(shù)意思是在完成了它預(yù)定的任務(wù)后才返回锯梁,在任務(wù)執(zhí)行時會阻塞當(dāng)前線程。而異步函數(shù)則是任務(wù)會完成但不會等它完成焰情,所以異步函數(shù)不會阻塞當(dāng)前線程陌凳,會繼續(xù)去執(zhí)行下一個函數(shù)。
4内舟、Concurrency & Parallelism
并發(fā)的意思就是同時運行多個任務(wù)合敦。這些任務(wù)可能是以在單核 CPU 上以分時(時間共享)的形式同時運行,也可能是在多核 CPU 上以真正的并行方式來運行验游。然后為了使單核設(shè)備也能實現(xiàn)這一點充岛,并發(fā)任務(wù)必須先運行一個線程,執(zhí)行一個上下文切換耕蝉,然后運行另一個線程或進程崔梗。并行則是真正意思上的多任務(wù)同時運行。
5垒在、Context Switch
Context Switch即上下文切換炒俱,一個上下文切換指當(dāng)你在單個進程里切換執(zhí)行不同的線程時存儲與恢復(fù)執(zhí)行狀態(tài)的過程。這個過程在編寫多任務(wù)應(yīng)用時很普遍,但會帶來一些額外的開銷权悟。
6、Dispatch Queues
GCD dispatch queues是一個強大的執(zhí)行多任務(wù)的工具推盛。Dispatch queue是一個對象峦阁,它可以接受任務(wù),并將任務(wù)以先進先出(FIFO)的順序來執(zhí)行耘成。Dispatch queue可以并發(fā)的或串行的執(zhí)行任意一個代碼塊榔昔,而且并發(fā)任務(wù)會像NSOperationQueue那樣基于系統(tǒng)負載來合適地并發(fā)進行,串行隊列同一時間則只執(zhí)行單一任務(wù)瘪菌。Dispatch queues內(nèi)部使用的是線程撒会,GCD 管理這些線程,并且使用Dispatch queues的時候师妙,我們都不需要自己創(chuàng)建線程诵肛。Dispatch queues相對于和線程直接通信的代碼優(yōu)勢是:Dispatch queues使用起來特別方便,執(zhí)行任務(wù)更加有效率默穴。
GCD 簡單用法
1怔檩、獲取主隊列
//獲取主隊列
dispatch_queue_t main_queue = dispatch_get_main_queue();
//主隊列是串行隊列;里面的任務(wù)會串行執(zhí)行
dispatch_async(main_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"A:%d",i);
sleep(1);
}
});
dispatch_async(main_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"B:%d",i);
sleep(1);
}
});
2、獲取全局隊列
//獲取全局隊列
dispatch_queue_t global_queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//全局隊列中添加兩個任務(wù)蓄诽,全局隊列是并發(fā)隊列薛训。
dispatch_async(global_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"C:%d",i);
sleep(1);
}
});
dispatch_async(global_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"D:%d",i);
sleep(1);
}
});
3、自定義串行隊列
//創(chuàng)建串行隊列
dispatch_queue_t serial_queue = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"%@",serial_queue.description);
//添加任務(wù)
dispatch_async(serial_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"E:%d",i);
sleep(1);
}
});
dispatch_async(serial_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"F:%d",i);
sleep(1);
}
});
4仑氛、自定義并發(fā)隊列
//創(chuàng)建一個并發(fā)隊列
dispatch_queue_t concurrent_queue = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
//添加并發(fā)任務(wù)
dispatch_async(concurrent_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"G:%d",i);
sleep(1);
}
});
dispatch_async(concurrent_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"H:%d",i);
sleep(1);
}
});
5乙埃、延遲執(zhí)行一個任務(wù)
//延遲多少秒
double delaySeconds=5;
//創(chuàng)建時間
dispatch_time_t delay_time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySeconds * NSEC_PER_SEC));
//執(zhí)行延遲任務(wù)
dispatch_after(delay_time, dispatch_get_main_queue(), ^{
NSLog(@"hello world");
});
6、調(diào)度監(jiān)聽任務(wù)完成
//獲取并發(fā)隊列
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建調(diào)度組
dispatch_group_t one_group = dispatch_group_create();
//通過調(diào)度組發(fā)起并發(fā)任務(wù)
dispatch_group_async(one_group, global_queue, ^{
for (int i=0; i<3; i++) {
NSLog(@"I:%d",i);
sleep(1);
}
});
dispatch_group_async(one_group, global_queue, ^{
for (int i=0; i<4; i++) {
NSLog(@"J:%d",i);
sleep(1);
}
});
dispatch_group_async(one_group, global_queue, ^{
for (int i=0; i<5; i++) {
NSLog(@"K:%d",i);
sleep(1);
}
});
//調(diào)度組完成任務(wù)通知
dispatch_group_notify(one_group, dispatch_get_main_queue(), ^{
NSLog(@"one_group完成任務(wù)");
});
}
7锯岖、調(diào)度等待任務(wù)完成
//獲取并發(fā)隊列
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建調(diào)度組
dispatch_group_t one_group = dispatch_group_create();
//進入調(diào)度組
dispatch_group_enter(one_group);
//發(fā)起并發(fā)任務(wù)
dispatch_async(global_queue, ^{
for (int i=0; i<3; i++) {
NSLog(@"L:%d",i);
sleep(1);
}
});
dispatch_async(global_queue, ^{
for (int i=0; i<4; i++) {
NSLog(@"M:%d",i);
sleep(1);
}
});
dispatch_async(global_queue, ^{
for (int i=0; i<5; i++) {
NSLog(@"N:%d",i);
sleep(1);
}
//離開調(diào)度組
dispatch_group_leave(one_group);
});
//調(diào)度組等待:等待過程中介袜,下面的代碼不會被執(zhí)行
dispatch_group_wait(one_group, DISPATCH_TIME_FOREVER);
NSLog(@"等到了!");
GCD常見用法和應(yīng)用場景
1、dispatch_async
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
// 一個異步的任務(wù)嚎莉,例如網(wǎng)絡(luò)請求米酬,耗時的文件操作等等
...
dispatch_async(dispatch_get_main_queue(), ^{
// UI刷新
...
});
});
這種用法非常常見,比如開啟一個異步的網(wǎng)絡(luò)請求趋箩,待數(shù)據(jù)返回后返回主隊列刷新UI赃额;又比如請求圖片,待圖片返回刷新UI等等叫确。
2跳芳、dispatch_after
// NSEC_PER_SEC,每秒有多少納秒竹勉。
// USEC_PER_SEC飞盆,每秒有多少毫秒。
// NSEC_PER_USEC,每毫秒有多少納秒吓歇。
// DISPATCH_TIME_NOW 從現(xiàn)在開始
// DISPATCH_TIME_FOREVE 永久
dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
// 在queue里面延遲執(zhí)行的一段代碼
...
});
這為我們提供了一個簡單的延遲執(zhí)行的方式孽水,比如在view加載結(jié)束延遲執(zhí)行一個動畫等等。
3城看、dispatch_once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行一次的任務(wù)
...
});
可以使用其創(chuàng)建一個單例
4女气、dispatch_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, ^{
// 異步任務(wù)1
});
dispatch_group_async(group, queue, ^{
// 異步任務(wù)2
});
dispatch_group_notify(group, mainQueue, ^{
// 任務(wù)完成后,在主隊列中做一些操作
...
});
上述的一種方式测柠,可以適用于自己維護的一些異步任務(wù)的同步問題
5炼鞠、 dispatch_semaphore_signal(信號量)
//傳遞的參數(shù)是信號量最初值,下面例子的信號量最初值是1
dispatch_semaphore_t signal = dispatch_semaphore_create(1);
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 當(dāng)信號量是0的時候,dispatch_semaphore_wait(signal, overTime);這句代碼會一直等待直到overTime超時.
//這里信號量是1 所以不會在這里發(fā)生等待.
dispatch_semaphore_wait(signal, overTime);
NSLog(@"需要線程同步的操作1 開始");
sleep(2);
NSLog(@"需要線程同步的操作1 結(jié)束");
long signalValue = dispatch_semaphore_signal(signal);//這句代碼會使信號值 增加1
//并且會喚醒一個線程去開始繼續(xù)工作,如果喚醒成功,那么返回一個非零的數(shù),如果沒有喚醒,那么返回 0
NSLog(@"%ld",signalValue);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(signal, overTime);
NSLog(@"需要線程同步的操作2");
dispatch_semaphore_signal(signal);
long signalValue = dispatch_semaphore_signal(signal);
NSLog(@"%ld",signalValue);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(signal, overTime);
NSLog(@"需要線程同步的操作3");
dispatch_semaphore_signal(signal);
long signalValue = dispatch_semaphore_signal(signal);
NSLog(@"%ld",signalValue);
});
利用 dispatch_semaphore_t signal 組織一個并發(fā)數(shù)是10 的一個多線程工作隊列.
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//注意這里信號量從10開始遞減,并不會阻塞循環(huán).循環(huán)10次,遞減到0的時候,開始阻塞.
NSLog(@"-------");
dispatch_group_async(group, queue, ^{
NSLog(@"%i",i);
sleep(1);
dispatch_semaphore_signal(semaphore);
});//創(chuàng)建一個新線程,并在線程結(jié)束后,發(fā)送信號量,通知阻塞的循環(huán)繼續(xù)創(chuàng)建新線程.
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
如此循環(huán)就形成了對并發(fā)的控制,如上就是一個并發(fā)數(shù)為10的一個線程隊列轰胁。
實戰(zhàn):利用 dispatch_semaphore_t signal 組織一個生產(chǎn)消費模式
__block int product = 0;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //消費者隊列
while (1) {
if(!dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER))){
////非 0的時候,就是成功的timeout了,這里判斷就是沒有timeout 成功的時候是 0
NSLog(@"消費%d產(chǎn)品",product);
product--;
};
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //生產(chǎn)者隊列
while (1) {
sleep(1); //wait for a while
product++;
NSLog(@"生產(chǎn)%d產(chǎn)品",product);
dispatch_semaphore_signal(sem);
}
});
6谒主、 dispatch_suspend和dispatch_resume
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_suspend(queue); //暫停隊列queue
dispatch_resume(queue); //恢復(fù)隊列queue