GCD的簡單易用,很容易愛不釋手今艺。今天趁機梳理一下GCD相關(guān)知識。
線程爵卒、任務(wù)和隊列
首先來認識一下隊列和任務(wù)
引自:行走的少年郎
任務(wù):就是執(zhí)行操作的意思虚缎,換句話說就是你在線程中執(zhí)行的那段代碼。在GCD中是放在block中的技潘。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行和異步執(zhí)行。兩者的主要區(qū)別是:是否具備開啟新線程的能力千康。
- 同步執(zhí)行(sync):只能在當(dāng)前線程中執(zhí)行任務(wù)享幽,不具備開啟新線程的能力
- 異步執(zhí)行(async):可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
隊列:隊列用來管理線程拾弃,也就是線程池值桩,控制線程的創(chuàng)建和執(zhí)行。這里的隊列指任務(wù)隊列豪椿,即用來存放任務(wù)的隊列奔坟。隊列是一種特殊的線性表,采用FIFO(先進先出)的原則搭盾,即新任務(wù)總是被插入到隊列的末尾咳秉,而讀取任務(wù)的時候總是從隊列的頭部開始讀取。每讀取一個任務(wù)鸯隅,則從隊列中釋放一個任務(wù)澜建。在GCD中有兩種隊列:串行隊列和并發(fā)隊列。
- 并發(fā)隊列(Concurrent Dispatch Queue):可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
- 串行隊列(Serial Dispatch Queue):讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后蝌以,再執(zhí)行下一個任務(wù))炕舵。
線程:可并發(fā)執(zhí)行的,擁有最小系統(tǒng)資源跟畅,共享進程資源的基本調(diào)度單位咽筋。
共用堆,自有棧(官方資料說明iOS主線程棧大小為1M徊件,其它線程為512K)奸攻。
并發(fā)執(zhí)行進度不可控,對非原子操作易造成狀態(tài)不一致虱痕,加鎖控制又有死鎖的風(fēng)險舞箍。
何為GCD
百度一下
Grand Central Dispatch (GCD)是Apple開發(fā)的一個多核編程的較新的解決方法。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)皆疹。它是一個在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)疏橄。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。
優(yōu)點
- 讓程序平行的排列各個任務(wù)捎迫,根據(jù)當(dāng)前的系統(tǒng)資源來合理的安排和分配任務(wù)
- 能充分利用多核CUP資源
- 自動管理線程的生命周期晃酒,程序員脫離了手動管理線程的苦海
使用
1、創(chuàng)建隊列
- 串行隊列:
使用dispatch_queue_create(const char * label, dispatch_queue_attr_t attr)
,label
參數(shù)表示此隊列的標(biāo)識符窄绒,調(diào)試的時候可以用到贝次,第二個參數(shù)attr
是類型標(biāo)識符,創(chuàng)建串行隊列時使用DISPATCH_QUEUE_SERIAL
,創(chuàng)建并發(fā)隊列時使用DISPATCH_QUEUE_CONCURRENT
彰导。
//創(chuàng)建一個串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("com.luqiuan", DISPATCH_QUEUE_SERIAL);
程序開始運行時會自己創(chuàng)建一個主線程放在主隊列中蛔翅,主隊列也是一個串行隊列,主隊列里只存在一個主線程位谋,我們的大部分代碼邏輯都可以在主線程里面處理山析,如果沒有特殊要求(比如耗時操作)我們一般在不需要創(chuàng)建其他線程來增加程序的復(fù)雜度√透福可以用dispatch_get_main_queue()
來獲取主隊列笋轨。
- 并發(fā)隊列
并發(fā)隊列的創(chuàng)建與串行相似,只需要改變第二個參數(shù)即可
//創(chuàng)建一個并發(fā)隊列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.luqiuan", DISPATCH_QUEUE_CONCURRENT);
還可以使用dispatch_get_global_queue
來創(chuàng)建全局并發(fā)隊列赊淑。GCD默認提供了全局的并發(fā)隊列爵政,需要傳入兩個參數(shù)。第一個參數(shù)表示隊列優(yōu)先級陶缺,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT
钾挟。第二個參數(shù)暫時沒用,用0即可饱岸。
2等龙、 任務(wù)的創(chuàng)建方法
// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
// 這里放任務(wù)代碼
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
// 這里放任務(wù)代碼
});
雖然使用GCD只需兩步,但是既然我們有兩種隊列伶贰,兩種任務(wù)執(zhí)行方式蛛砰,那么我們就有了四種不同的組合方式。這四種不同的組合方式是
并發(fā)隊列 + 同步執(zhí)行 : 不會創(chuàng)建新線程黍衙,任務(wù)加入隊列時會立馬串行執(zhí)行
并發(fā)隊列 + 異步執(zhí)行 : 具備創(chuàng)建新線程的能力泥畅,先將任務(wù)加入隊列后再開始并行執(zhí)行
串行隊列 + 同步執(zhí)行 : 不會創(chuàng)建新線程,先將任務(wù)加入隊列后再串行執(zhí)行
串行隊列 + 異步執(zhí)行 : 具備創(chuàng)建新線程的能力琅翻,先將任務(wù)加入隊列后再串行執(zhí)行
主隊列 + 同步執(zhí)行 : 不會創(chuàng)建新線程位仁,任務(wù)串行執(zhí)行
主隊列 + 異步執(zhí)行 : 不會創(chuàng)建新線程,任務(wù)串行執(zhí)行
GCD其他功能
1方椎、延時操作
//延時執(zhí)行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延時兩秒");
});
2聂抢、保證代碼執(zhí)行的唯一性
//保證代碼只執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"這段代碼只執(zhí)行一次");
});
3、GCD快讀迭代
//GCD快讀迭代棠众,當(dāng)我們用for循環(huán)便利一個數(shù)組時琳疏,一般會按順序一個一個便利數(shù)組元素有决,使用GCD迭代可以提高速率,同時遍歷多個元素
dispatch_queue_t ser1 = createQueue();
NSArray *arr = @[@"一",@"二",@"三",@"四",@"五",@"六",@"七",@"八",@"九"];
dispatch_apply(9, ser1, ^(size_t index) {
NSLog(@"%@",arr[index]);
});
4空盼、GCD的柵欄方法
當(dāng)我們需要等待并發(fā)隊列中兩個異步任務(wù)都完成時书幕,才執(zhí)行某一操作時,可以使用GCD的柵欄dispatch_barrier_async
揽趾,柵欄就像一個判斷器只有當(dāng)隊列中柵欄前面的所有任務(wù)執(zhí)行完成時台汇,才會執(zhí)行后面的任務(wù)。柵欄只能在用來同一個隊列中篱瞎,如果是不同隊列中的任務(wù)苟呐,我們可以只用GCD隊列組,稍后介紹俐筋。
NSLog(@"========begin===");
dispatch_queue_t serQueue = createQueue();
dispatch_async(serQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"第一個任務(wù)%d",i);
sleep(4);
}
});
dispatch_async(serQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"第二個任務(wù)%d",i);
sleep(1);
}
});
//用同步的話會阻塞主線程,所以采用異步執(zhí)行
dispatch_barrier_async(serQueue,^{
NSLog(@"柵欄柵欄--------------");
});
dispatch_async(serQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"第三個任務(wù)%d",i);
sleep(1);
}
});
NSLog(@"========end=====");
4牵素、GCD隊列組
上面說的是等待同一隊列中不同的并發(fā)任務(wù)執(zhí)行完,而隊列組我們可以用于等待不同隊列中的任務(wù)執(zhí)行完時成時再執(zhí)行某一操作校哎,直接上代碼:
//GCD隊列組两波,當(dāng)我們需要等兩個耗時操作完成時再回到指定隊列工作時瞳步,我們可以使用GCD隊列組
dispatch_group_t gcdGroup = dispatch_group_create();
dispatch_queue_t serQueue = dispatch_queue_create("com.luqiuan", DISPATCH_QUEUE_SERIAL);
//serQueue隊列中的任務(wù)1
dispatch_group_async(gcdGroup, serQueue, ^{
NSLog(@"第一個耗時操作");
sleep(3);
});
//全局隊列中的任務(wù)2
dispatch_group_async(gcdGroup, dispatch_get_global_queue(0, 0), ^{
sleep(2);
NSLog(@"第二個耗時操作");
sleep(3);
});
//任務(wù)1和任務(wù)2都完成時闷哆,回到主線程執(zhí)行
dispatch_group_notify(gcdGroup, dispatch_get_main_queue() , ^{
NSLog(@"回到主線程");
});
5、GCD設(shè)置上下文和清理函數(shù)
//設(shè)置上下文
void dispatch_set_context(dispatch_object_t object, void *context);
//獲取上下文
void *dispatch_get_context(dispatch_object_t object);
//清理函數(shù)单起,當(dāng) queue 的引用計數(shù)到達 0 時執(zhí)行清理函數(shù)
void dispatch_set_finalizer_f(dispatch_object_t object,dispatch_function_t finalizer);
代碼
// 當(dāng) queue 的引用計數(shù)到達 0 時執(zhí)行清理函數(shù)
void finalizerFunction(void *context) {
char *theData = (char *)context;
printf("myFinalizerFunction - data = %s\n", theData);
// 具體清理細節(jié)可以另寫一個函數(shù)
myCleanUpDataContextFunction(theData);
}
// 具體清理細節(jié)
void myCleanUpDataContextFunction(char *data) {
printf("myCleanUpDataContextFunction - data = %s\n", data);
}
dispatch_queue_t createQueue(){
char *data = "我是一個字符串";
dispatch_queue_t serialQueue = dispatch_queue_create("com.luqiuan", DISPATCH_QUEUE_CONCURRENT);
if (serialQueue) {
dispatch_set_context(serialQueue, data);
dispatch_set_finalizer_f(serialQueue, &finalizerFunction);
}
return serialQueue;
}
void main(){
dispatch_queue_t serialQueue = createQueue();
char *name = dispatch_get_context(serialQueue);
NSLog(@"==%@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
}