什么是GCD:
GCD的全稱是Grand Central Dispatch,可譯為“牛逼的中樞調度器”;
純C語言,提供了非常多強大的函數(shù);
GCD是蘋果公司為多核的并行運算提出的解決方案;
GCD會自動利用更多的CPU內核(比如雙核、四核);
GCD會自動管理線程的生命周期(創(chuàng)建線程、調度任務、銷毀線程);
程序員只需要告訴GCD想要執(zhí)行什么任務,不需要編寫任何線程管理代碼;
GCD中的兩大核心
任務:執(zhí)行什么操作
隊列:用來存放任務
GCD的使用步驟
定制任務:確定想做的事情
將任務添加到隊列中:<1>GCD會自動將隊列中的任務取出,放到對應的線程中執(zhí)行。<2>任務的取出遵循隊列的FIFO原則,及先進先出,后進后出。
GCD中兩個用來執(zhí)行任務的常用函數(shù)
同步的方法執(zhí)行任務,同步函數(shù)
dispatch_sync(dispatch_queue_t _Nonnull queue, dispatch_block_t block);
異步的方法執(zhí)行任務,異步函數(shù)
dispatch_async(dispatch_queue_t _Nonnull queue, dispatch_block_t block);
block---任務
同步和異步的區(qū)別:
同步:只能在當前線程中執(zhí)行任務,不具備開啟新線程的能力
異步:可以在新的線程中執(zhí)行任務,具備開啟新線程的能力(具備但是不一定非要開)
-GCD的隊列可分為兩種
并發(fā)隊列:<1>可以讓多個任務并發(fā)(同時)執(zhí)行(自動開啟對個線程同時執(zhí)行任務,只要第一個任務取出來之后,不在等待執(zhí)行完畢就可以接著取第二個任務) <2>并發(fā)功能只能在異步函數(shù)下才有效
串行隊列:讓任務一個接著一個執(zhí)行(一個任務執(zhí)行完畢后,再執(zhí)行下一個任務)
GCD的幾種組合使用
- 異步函數(shù)+并發(fā)隊列
/**
異步函數(shù)+并發(fā)隊列:會開啟多條線程,所有的任務并發(fā)執(zhí)行
//注意:開幾條線程并不是由任務數(shù)量決定的,是由GCD內部自動決定的
//輸出結果(number=1的才是主線程,其他的編號是由于Xcode添加的)
2019-02-21 11:16:43.478993+0800 SmallProgram[3127:62594] 3---當前線程<NSThread: 0x600001719700>{number = 3, name = (null)}
2019-02-21 11:16:43.479036+0800 SmallProgram[3127:62593] 2---當前線程<NSThread: 0x6000017e1840>{number = 8, name = (null)}
2019-02-21 11:16:43.479042+0800 SmallProgram[3127:62611] 4---當前線程<NSThread: 0x6000017de5c0>{number = 9, name = (null)}
2019-02-21 11:16:43.479061+0800 SmallProgram[3127:62610] 1---當前線程<NSThread: 0x6000017fbb00>{number = 6, name = (null)}
*/
-(void)asyncConcurrent{
//01--創(chuàng)建隊列
/**
參數(shù)說明
第一個參數(shù):C語言的字符串,給隊列起個名字,切記不要添加@,因為是C語言的
第二個參數(shù):類型 DISPATCH_QUEUE_CONCURRENT 并發(fā)隊列
DISPATCH_QUEUE_SERIAL 串行隊列
*/
dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_CONCURRENT);
//02--封裝任務,把任務添加到隊列
dispatch_async(queue, ^{
NSLog(@"1---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4---當前線程%@",[NSThread currentThread]);
});
}
- 異步函數(shù)+串行隊列
/**
異步函數(shù)+串行隊列:會開啟一條子線程,所有的任務在該子線程中串行執(zhí)行
//輸出結果(number=1的才是主線程,其他的編號是由于Xcode添加的)
2019-02-21 11:21:50.500116+0800 SmallProgram[3205:66377] 1---當前線程<NSThread: 0x600000f30740>{number = 7, name = (null)}
2019-02-21 11:21:50.500287+0800 SmallProgram[3205:66377] 2---當前線程<NSThread: 0x600000f30740>{number = 7, name = (null)}
2019-02-21 11:21:50.500399+0800 SmallProgram[3205:66377] 3---當前線程<NSThread: 0x600000f30740>{number = 7, name = (null)}
2019-02-21 11:21:50.500515+0800 SmallProgram[3205:66377] 4---當前線程<NSThread: 0x600000f30740>{number = 7, name = (null)}
*/
-(void)asyncSerial{
//01--創(chuàng)建隊列
/**
參數(shù)說明
第一個參數(shù):C語言的字符串,給隊列起個名字,切記不要添加@,因為是C語言的
第二個參數(shù):類型 DISPATCH_QUEUE_CONCURRENT 并發(fā)隊列
DISPATCH_QUEUE_SERIAL 串行隊列
*/
dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL);
//02--封裝任務,把任務添加到隊列
dispatch_async(queue, ^{
NSLog(@"1---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4---當前線程%@",[NSThread currentThread]);
});
}
同步函數(shù)+并發(fā)隊列
/**
同步函數(shù)+并發(fā)隊列:(同步函數(shù)不具備開始線程)不會開啟子線程,所有的任務在當前線程中串行執(zhí)行
//輸出結果(number=1的才是主線程,其他的編號是由于Xcode添加的)
2019-02-21 11:30:45.546540+0800 SmallProgram[3349:71630] 1---當前線程<NSThread: 0x600003dd6940>{number = 1, name = main}
2019-02-21 11:30:45.546734+0800 SmallProgram[3349:71630] 2---當前線程<NSThread: 0x600003dd6940>{number = 1, name = main}
2019-02-21 11:30:45.546859+0800 SmallProgram[3349:71630] 3---當前線程<NSThread: 0x600003dd6940>{number = 1, name = main}
2019-02-21 11:30:45.546976+0800 SmallProgram[3349:71630] 4---當前線程<NSThread: 0x600003dd6940>{number = 1, name = main}
*/
-(void)syncConcurrent{
//01--創(chuàng)建隊列
/**
參數(shù)說明
第一個參數(shù):C語言的字符串,給隊列起個名字,切記不要添加@,因為是C語言的
第二個參數(shù):類型 DISPATCH_QUEUE_CONCURRENT 并發(fā)隊列
DISPATCH_QUEUE_SERIAL 串行隊列
*/
dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_CONCURRENT);
//02--封裝任務,把任務添加到隊列
dispatch_sync(queue, ^{
NSLog(@"1---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"4---當前線程%@",[NSThread currentThread]);
});
}
- 同步函數(shù)+串行隊列
/**
同步函數(shù)+串行隊列:(同步函數(shù)不具備開始線程)不會開啟子線程,所有的任務在當前線程中串行執(zhí)行
//輸出結果(number=1的才是主線程,其他的編號是由于Xcode添加的)
2019-02-21 11:33:36.127713+0800 SmallProgram[3420:73690] 1---當前線程<NSThread: 0x600003c00d80>{number = 1, name = main}
2019-02-21 11:33:36.127911+0800 SmallProgram[3420:73690] 2---當前線程<NSThread: 0x600003c00d80>{number = 1, name = main}
2019-02-21 11:33:36.128021+0800 SmallProgram[3420:73690] 3---當前線程<NSThread: 0x600003c00d80>{number = 1, name = main}
2019-02-21 11:33:36.128148+0800 SmallProgram[3420:73690] 4---當前線程<NSThread: 0x600003c00d80>{number = 1, name = main
*/
-(void)syncSerial{
//01--創(chuàng)建隊列
/**
參數(shù)說明
第一個參數(shù):C語言的字符串,給隊列起個名字,切記不要添加@,因為是C語言的
第二個參數(shù):類型 DISPATCH_QUEUE_CONCURRENT 并發(fā)隊列
DISPATCH_QUEUE_SERIAL 串行隊列
*/
dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL);
//02--封裝任務,把任務添加到隊列
dispatch_sync(queue, ^{
NSLog(@"1---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"4---當前線程%@",[NSThread currentThread]);
});
}
- 異步函數(shù)+主隊列
/**
異步函數(shù)+主隊列:不會開線程,所有任務都在主線程中串行執(zhí)行
//輸出結果
2019-02-21 13:37:50.406381+0800 SmallProgram[5481:145502] 測試1
2019-02-21 13:37:50.406381+0800 SmallProgram[5481:145502] 測試2
2019-02-21 13:37:50.406381+0800 SmallProgram[5100:128550] 1---當前線程<NSThread: 0x60000336d7c0>{number = 1, name = main}
2019-02-21 13:37:50.406613+0800 SmallProgram[5100:128550] 2---當前線程<NSThread: 0x60000336d7c0>{number = 1, name = main}
2019-02-21 13:37:50.406778+0800 SmallProgram[5100:128550] 3---當前線程<NSThread: 0x60000336d7c0>{number = 1, name = main}
2019-02-21 13:37:50.406936+0800 SmallProgram[5100:128550] 4---當前線程<NSThread: 0x60000336d7c0>{number = 1, name = main}
*/
-(void)asyncMain{
//01--創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"測試1");
//02--封裝任務,把任務添加到隊列
dispatch_async(queue, ^{
NSLog(@"1---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3---當前線程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4---當前線程%@",[NSThread currentThread]);
});
NSLog(@"測試2");
}
- 同步函數(shù)+主隊列(會崩潰):
/**
同步函數(shù)+主隊列:
情況一:在主線程調用syncMain
分析:不會開線程,這段代碼會死鎖崩潰(隊列為主隊列,所以只能在主線程中執(zhí)行,但是當前主線程在執(zhí)行syncMain方法,被占用著,因為當前函數(shù)還未運行完),此時思考下異步+主隊列為啥不死鎖?這個面試官很愛問的。答案是:因為是異步的,所以程序可以先執(zhí)行完一個任務在執(zhí)行另外一個任務。
解釋:當主隊列中有任務的時候,主隊列就會安排主線程來來執(zhí)行該任務,但是在調度之前會先檢查主線程的狀態(tài)(是否在忙),如果主線程當前在忙,那么暫停調度,值到主線程空閑為止。
情況二:在子線程調用syncMain
分析:因為在走dispatch_sync方法的時候主線程在空閑狀態(tài)。
輸出結果:
2019-02-21 13:55:13.366052+0800 SmallProgram[5384:140751] 1---當前線程<NSThread: 0x60000124d000>{number = 1, name = main}
2019-02-21 13:55:13.366318+0800 SmallProgram[5384:140751] 2---當前線程<NSThread: 0x60000124d000>{number = 1, name = main}
2019-02-21 13:55:13.366544+0800 SmallProgram[5384:140751] 3---當前線程<NSThread: 0x60000124d000>{number = 1, name = main}
2019-02-21 13:55:13.366828+0800 SmallProgram[5384:140751] 4---當前線程<NSThread: 0x60000124d000>{number = 1, name = main}
*/
-(void)syncMain{
//01--創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_main_queue();
//02--封裝任務,把任務添加到隊列
dispatch_sync(queue, ^{
NSLog(@"1---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3---當前線程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"4---當前線程%@",[NSThread currentThread]);
});
}
- GCD的日常幾點使用
GCD的一次性代碼:
-(void)once{
//整個程序運行過程中只會執(zhí)行一次+本身是線程安全的
//應用:單例模式
//內部原理:通過判斷onceToken的值來決定是否執(zhí)行block中的任務,只有在第一次為0,其它都為-1
static dispatch_once_t onceToken;
NSLog(@"%zd",onceToken);
dispatch_once(&onceToken, ^{
//任務
});
}
- GCD的延遲執(zhí)行:
-(void)delay{
/**
GCD的延遲執(zhí)行
@param when#> 設置時間(CGD中的時間單位為納秒) description#>
@param queue#> 隊列(決定block塊紅的任務在哪個線程中執(zhí)行,如果主隊列就在主線程,否則在子線程) description#>
@param void 執(zhí)行的任務
原理:先等2秒,然后在吧任務提交到隊列,如果先提交到隊列,那么任務不好控制,并且很好隊列的資源
*/
NSLog(@"hahaha");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"hahaha%@",[NSThread currentThread]);
});
}
GCD的快速迭代:(小應用:將一個文件夾下多張圖片剪切到另外一個文件夾下)
//快速迭代(遍歷)
-(void)apply{
//在當前線程中串行執(zhí)行----0---<NSThread: 0x600003b9d680>{number = 1, name = main}
for(int i=0;i<10;i++){
NSLog(@"%d---%@",i,[NSThread currentThread]);
}
/**
//快速迭代--會開啟多條子線程和主線程一起并發(fā)的執(zhí)行任務
iterations#> 遍歷的次數(shù)
queue#> 隊列 --如果是主隊列則死鎖,如果是串行隊列則跟for循環(huán)一樣
size_t 索引
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t i) {
NSLog(@"%zd---%@",i,[NSThread currentThread]);
});
}
- GCD的柵欄函數(shù)()
-(void)barrier{
//柵欄函數(shù)
//需求:有一個新任務打印00000的任務,要求在1 2執(zhí)行完之后執(zhí)行,要保證該任務執(zhí)行完之后才能執(zhí)行后面的3 4任務。
//講解:柵欄前面的任務并發(fā)執(zhí)行,后面的任務也是并發(fā)執(zhí)行,當前面的任務執(zhí)行完之后執(zhí)行柵欄函數(shù)中的任務,等該任務執(zhí)行完畢后在執(zhí)行后面的任務。
//警告:不能使用全局并發(fā)隊列(dispatch_get_global_queue(0, 0)),否則不能攔截
//01獲得隊列
//
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
//02封裝任務,并且添加到隊列
dispatch_async(queue, ^{
NSLog(@"01---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"02---%@",[NSThread currentThread]);
});
//柵欄函數(shù)
dispatch_barrier_async(queue, ^{
NSLog(@"00000");
});
dispatch_async(queue, ^{
NSLog(@"03---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"04---%@",[NSThread currentThread]);
});
}
GCD的隊列組(控制多條隊列中任務都完成在執(zhí)行新的,可用在下載多張小圖片,然后都下載完成后拼接起來)
//GCD的隊列組
-(void)group{
//需求:有5個任務,在多個隊列的子線程中并發(fā)執(zhí)行,添加打印00000的任務,必須在所有任務完成后在執(zhí)行
//增加需求:攔截多個隊列中的任務
//01 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue01 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue02 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue01, ^{
NSLog(@"01---%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue01, ^{
NSLog(@"02---%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue01, ^{
NSLog(@"03---%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue02, ^{
NSLog(@"04---%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue02, ^{
NSLog(@"05---%@",[NSThread currentThread]);
});
//03 攔截通知,當所有的任務都執(zhí)行完畢然后打印00000
//注意:通知中的第二個參數(shù)能控制執(zhí)行最后的任務在子線程還是主線程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"00000--%@",[NSThread currentThread]);
});
}
總結:
同步函數(shù):
+串行隊列:不會開線程,所有任務在當前線程串行執(zhí)行
+并發(fā)隊列:不會開線程,所有任務在當前線程串行執(zhí)行
+主隊列:死鎖
異步函數(shù):
+串行隊列:會開1條線程,所有任務在子線程中串行執(zhí)行
+并發(fā)隊列:會開N條線程,所有任務在子線程中并發(fā)執(zhí)(注意:線程的數(shù)量并不等于任務的數(shù)量)
+主隊列:不會開線程,所有任務在主線程串行執(zhí)行