- OK吁津,既然講到多線程铆隘,那就一次性把全部多線程的內(nèi)容介紹完畢~
?? 簡介
-
<h6>什么是GCD</h6>
- 全稱是Grand Central Dispatch磅叛,可譯為“牛逼的中樞調(diào)度器”
- 純C語言睦疫,提供了非常多強(qiáng)大的函數(shù)
-
<h6>GCD的優(yōu)勢</h6>
- GCD是蘋果公司為
多核
的并發(fā)
運(yùn)算提出的解決方案 - GCD會自動(dòng)利用更多的CPU內(nèi)核(比如雙核道盏、四核)
- GCD會自動(dòng)管理線程的生命周期(創(chuàng)建線程莹菱、調(diào)度任務(wù)移国、銷毀線程)
- 程序猿只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
- GCD是蘋果公司為
-
<h6>任務(wù)和隊(duì)列</h6>
-
任務(wù)
:執(zhí)行什么操作 -
隊(duì)列
:用來存放任務(wù)
-
-
<h6>GCD的使用就2個(gè)步驟</h6>
- 定制
任務(wù)
- 確定想要做的事情
- 將
任務(wù)
添加到隊(duì)列
中- GCD會自動(dòng)將
隊(duì)列
中的任務(wù)
取出道伟,放到對應(yīng)的線程
中執(zhí)行 -
任務(wù)
的取出遵循隊(duì)列
的FIFO
原則:先進(jìn)先出迹缀,后進(jìn)后出
- GCD會自動(dòng)將
- 定制
-
<h6>執(zhí)行任務(wù)</h6>
- GCD中有2個(gè)用來執(zhí)行任務(wù)的常用函數(shù)
<h6>異步函數(shù)</h6>
// 異步函數(shù) + 并發(fā)隊(duì)列: 開啟多條線程并發(fā)執(zhí)行隊(duì)列中的任務(wù)
// !!!*** 線程的數(shù)量并不是有任務(wù)的數(shù)量決定的使碾,而是由系統(tǒng)決定的
- (void)asyncConcurrent {
- 1.1 獲得并發(fā)隊(duì)列-------------第一種方式
/**
第一個(gè)參數(shù) C語言字符串 隊(duì)列表的名稱(一般用公司域名:com.vn_vincent.www.DownloadQueue)
第二個(gè)參數(shù) 隊(duì)列的類型
DISPATCH_QUEUE_CONCURRENT:并發(fā)隊(duì)列
DISPATCH_QUEUE_SERIAL:串行隊(duì)列
*/
dispatch_queue_t queue = dispatch_queue_create("com.vn_vincent.www.DownLoadQueue", DISPATCH_QUEUE_CONCURRENT);
- 1.2 獲得并發(fā)隊(duì)列-----------------第二種方式
/**
第一個(gè)參數(shù):隊(duì)列的優(yōu)先級,一般傳default/0
第二個(gè)參數(shù):留給未來使用的祝懂,總是傳一個(gè)0 就可以了
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- 2.0 封裝任務(wù) 并把任務(wù)添加到隊(duì)列中
dispatch_async(queue, ^{
NSLog(@"downLoad1-------------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"downLoad2-------------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"downLoad3-------------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"downLoad4-------------%@", [NSThread currentThread]);
});
}
// 異步函數(shù) + 串行隊(duì)列: 開啟一條子線程票摇,串行執(zhí)行任務(wù)
- (void)asyncSerial {
- 1.0 獲得并發(fā)隊(duì)列
/**
第一個(gè)參數(shù) C語言字符串 隊(duì)列表的名稱(一般用公司域名:com.vn_vincent.www.DownloadQueue)
第二個(gè)參數(shù) 隊(duì)列的類型 description#>
DISPATCH_QUEUE_CONCURRENT:并發(fā)隊(duì)列
DISPATCH_QUEUE_SERIAL:串行隊(duì)列
*/
dispatch_queue_t queue = dispatch_queue_create("com.vn_vincent.www.DownLoadQueue", DISPATCH_QUEUE_SERIAL);
- 2.0 封裝任務(wù) 并把任務(wù)添加到隊(duì)列中
dispatch_async(queue, ^{
NSLog(@"downLoad1-------------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"downLoad2-------------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"downLoad3-------------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"downLoad4-------------%@", [NSThread currentThread]);
});
}
// 異步函數(shù) + 主隊(duì)列:不會開子線程,所有任務(wù)都在主線程中串行執(zhí)行
- (void)asyncMain {
- 1.0 獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
- 2.0封裝任務(wù) 并把任務(wù)封裝到隊(duì)列中去
dispatch_sync(queue, ^{
NSLog(@"downLoad1-----------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad2-----------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad3-----------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad4-----------------%@", [NSThread currentThread]);
});
}
<b>同步函數(shù):dispatch_sync
- 不具備開子線程的能力砚蓬,可以開子線程
- 執(zhí)行任務(wù)的方式:同步
異步函數(shù):dispatch_async
- 具備開線程的能力
- 執(zhí)行任務(wù)的方式:異步
隊(duì)列的類型
-
“并發(fā)隊(duì)列”:隊(duì)列中的任務(wù)可以同時(shí)執(zhí)行(異步)
- 獲得方法:
1?? 自己創(chuàng)建(concurrent)
2?? 全局并發(fā)隊(duì)列
- 獲得方法:
-
“串行隊(duì)列”:隊(duì)列中的任務(wù)只能一個(gè)接著一個(gè)的執(zhí)行
- 獲得方法:
1?? 自己創(chuàng)建(serial)
2?? 主隊(duì)列:串行 | 在主隊(duì)列中的所有的任務(wù)都必須在主隊(duì)列中執(zhí)行 | 調(diào)度時(shí)比較特殊(矢门!)
</b>
- 獲得方法:
<b>同步函數(shù)</b>
// 同步函數(shù) + 并發(fā)隊(duì)列: 不會開子線程,所有任務(wù)在當(dāng)前線程中串行執(zhí)行
- (void)syncConcurrent {
- 1.0 獲得并發(fā)隊(duì)列
/**
第一個(gè)參數(shù) C語言字符串 隊(duì)列表的名稱(一般用公司域名:com.vn_vincent.www.DownloadQueue)
第二個(gè)參數(shù) 隊(duì)列的類型
DISPATCH_QUEUE_CONCURRENT:并發(fā)隊(duì)列
DISPATCH_QUEUE_SERIAL:串行隊(duì)列
*/
dispatch_queue_t queue = dispatch_queue_create("com.vn_vincent.www.DownLoadQueue", DISPATCH_QUEUE_CONCURRENT);
- 2.0 封裝任務(wù) 并把任務(wù)添加到隊(duì)列中去
dispatch_sync(queue, ^{
NSLog(@"downLoad1-------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2-------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad3-------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download4-------------%@", [NSThread currentThread]);
});
}
// 同步函數(shù) + 串行隊(duì)列: 不會開子線程灰蛙,所以任務(wù)在當(dāng)前線程中串行執(zhí)行
- (void)syncSerial {
- 1.0 獲得并發(fā)隊(duì)列
/**
第一個(gè)參數(shù) C語言字符串 隊(duì)列表的名稱(一般用公司域名:com.vn_vincent.www.DownloadQueue)
第二個(gè)參數(shù) 隊(duì)列的類型 description#>
DISPATCH_QUEUE_CONCURRENT:并發(fā)隊(duì)列
DISPATCH_QUEUE_SERIAL:串行隊(duì)列
*/
dispatch_queue_t queue = dispatch_queue_create("com.vn_vincent.www.DownloadQueue", DISPATCH_QUEUE_SERIAL);
- 2.0 封裝任務(wù) 并把任務(wù)添加到隊(duì)列中去
dispatch_sync(queue, ^{
NSLog(@"downLoad1-------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad2-------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad3-------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad4-------------%@", [NSThread currentThread]);
});
}
// 同步函數(shù) + 主隊(duì)列:會發(fā)生死鎖
- (void)syncMain {
- 1.0 獲取主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"start");
// **!! 當(dāng)主隊(duì)列中有任務(wù)要執(zhí)行的時(shí)候祟剔,會安排主線程來執(zhí)行任務(wù),\
但是在安排之前會先檢查主線程的狀態(tài)摩梧,如果空閑則執(zhí)行物延,否則就暫停調(diào)度 \
等待到主線程空閑為止
- 2.0 封裝任務(wù) 并把任務(wù)封裝到隊(duì)列中去
dispatch_sync(queue, ^{
NSLog(@"downLoad1-----------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad2-----------------%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"downLoad3-----------------%@", [NSThread currentThread]);
});
}
- <b>GCD線程間通信</b>
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
- GCD開子線程— 1.0(并發(fā)|串行)獲得隊(duì)列
// DISPATCH_QUEUE_PRIORITY_DEFAULT == 0
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
- 2.0 封裝任務(wù),異步 | 完成線程間通信
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:@""];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
// 回到主線程刷新UI
// 下面方法也可以實(shí)現(xiàn)線程間通信
// [self performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>]
// dispatch_get_main_queue()
// 異步函數(shù)(同步函數(shù)) + 主隊(duì)列
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
}
- <b>GCD中常見的函數(shù)</b></br>
- 一次性函數(shù) 1)整個(gè)程序運(yùn)行中只會執(zhí)行一次 2)線程安全
- (void)once {
NSLog(@"start");
static dispatch_once_t onceToken;
// 內(nèi)部實(shí)現(xiàn)原理:最開始 onceToken == 0障本;如果onceToken == 0教届,那么就執(zhí)行一次下面的block代碼塊,onceToken == -1
dispatch_once(&onceToken, ^{
NSLog(@"once");
});
}
- GCD延遲操作
- (void)defer {
NSLog(@"start");
- 延遲的第一種方法:NSTimer
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(tesk) userInfo:nil repeats:NO];
- 延遲的第二種方法:
[self performSelector:@selector(tesk) withObject:nil afterDelay:2.0];
- 延遲的第三種方法:GCD
// * == 乘 GCD中事件事件單位為納秒(nm)
/**
主隊(duì)列:主線程
全局并發(fā)隊(duì)列:子線程
自己創(chuàng)建的并發(fā)隊(duì)列:子線程
自己創(chuàng)建的穿行隊(duì)列:子線程
dispatch_after:本質(zhì)上是一個(gè)異步函數(shù)
*/
// dispatch_queue_t queue = dispatch_get_main_queue();
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// dispatch_queue_t queue = dispatch_queue_create("com.vn_vincent.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = dispatch_queue_create("com.vn_vincent.www", DISPATCH_QUEUE_SERIAL);
// 內(nèi)部實(shí)現(xiàn)原理:延遲2秒驾霜,然后再把任務(wù)提交到隊(duì)列中
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
NSLog(@"GCD---------%@", [NSThread currentThread]);
});
}
- (void) tesk {
NSLog(@"tesk");
}
- GCD迭代
- (void)apply {
// 迭代 == 遍歷
// 特點(diǎn):在當(dāng)前線程中串行完成
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"%zd-------------%@", i, [NSThread currentThread]);
}
NSLog(@"----------------------------------");
/**
GCD的快速迭代*(開發(fā)中一般使用并發(fā)隊(duì)列):會開啟子線程和主線程一起執(zhí)行任務(wù),所有的任務(wù)并發(fā)執(zhí)行
第一個(gè)參數(shù) 遍歷次數(shù)
第二個(gè)參數(shù) 隊(duì)列 ~ 線程
傳入的隊(duì)列:自己創(chuàng)建的并發(fā)隊(duì)列 == 全局并發(fā)隊(duì)列
自己創(chuàng)建的串行隊(duì)列 == for in 循環(huán) (不會開啟子線程所有任務(wù)买置,串行執(zhí)行)
主隊(duì)列:產(chǎn)生死鎖
第三個(gè)參數(shù) size_t 類似于for遍歷的中的 i
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t i) {
NSLog(@"%zd-------------%@", i, [NSThread currentThread]);
});
}
- GCD快速迭代應(yīng)用
- (void)moveImage {
// 需求粪糙,桌面上有兩個(gè)文件夾(oldImage|newImage),要求忿项,將其中一個(gè)文件夾(oldImage)內(nèi)的所有文件剪切到另一個(gè)文件夾(newImage)中
// 01.得到上級文件夾的路徑
NSString *oldPath = @"/Users/Vincent/Desktop/oldImage";
NSString *newPath = @"/Users/Vincent/Desktop/newImage";
// 02.獲得所有文件
NSArray *subPaths = [[NSFileManager defaultManager] subpathsAtPath:oldPath];
NSLog(@"%@", subPaths);
// 03.便利并剪切文件
dispatch_apply(subPaths.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSString *fileName = subPaths[index];
// 拼接字符串得到文件名
NSString *oldFullPath = [oldPath stringByAppendingPathComponent:fileName];
NSString *newFullPath = [newPath stringByAppendingPathComponent:fileName];
// 04.剪切文件
[[NSFileManager defaultManager] moveItemAtPath:oldFullPath toPath:newFullPath error:nil];
NSLog(@"%@-------%@----------%@", oldFullPath, newFullPath, [NSThread currentThread]);
});
}
- GCD柵欄函數(shù)使用
// 柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
- (void) barrier {
// 需求:1)有四個(gè)任務(wù)蓉冈,開子線程并發(fā)的執(zhí)行這四個(gè)任務(wù)
// 2) 添加任務(wù)“臨時(shí)任務(wù)”,但是要求必須要等1|2任務(wù)完成后執(zhí)行轩触,必須要等臨時(shí)任務(wù)完成寞酿,才能執(zhí)行后面的任務(wù)
// 3)所有的任務(wù)都在子線程中完成(dispatch_async);
// 4)"臨時(shí)任務(wù)“需要在子線程中完成(dispatch_sync);
- 01.獲得并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.vn_vincent.www.TextQueue", DISPATCH_QUEUE_CONCURRENT);
- 02.異步函數(shù)
dispatch_async(queue, ^{
NSLog(@"任務(wù)1完成--------------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2完成--------------%@", [NSThread currentThread]);
});
// 柵欄函數(shù) Barrier
dispatch_barrier_async(queue, ^{
NSLog(@"+++++++臨時(shí)任務(wù)+++++++++, %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3完成--------------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4完成--------------%@", [NSThread currentThread]);
});
}
- GCD 隊(duì)列組
- (void)groupQueue01 {
- 01.創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
- 02.創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
- 03.封裝任務(wù),并把任務(wù)添加到異步隊(duì)列
/**
1) 封裝任務(wù)
2) 把任務(wù)添加到隊(duì)列中
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
*/
/**
1) 封裝任務(wù)
2) 把任務(wù)添加到隊(duì)列中
3)會監(jiān)聽任務(wù)的執(zhí)行情況脱柱,通知group
*/
dispatch_group_async(group, queue, ^{
NSLog(@"1------------------%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"2------------------%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"3------------------%@", [NSThread currentThread]);
});
// 攔截通知伐弹,當(dāng)隊(duì)列中所有任務(wù)的任務(wù)都執(zhí)行完畢的時(shí)候會進(jìn)行下面的任務(wù)執(zhí)行
// 內(nèi)部本身是異步的,不會堵塞
dispatch_group_notify(group, queue, ^{
NSLog(@"----------------dispatch_group_notify------------------");
});
// 攔截通知的另一種方法
// DISPATCH_TIME_FOREVER 等待榨为,死等.直到隊(duì)列中所有的任務(wù)都執(zhí)行完畢之后才會執(zhí)行
// 阻塞的
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"------------end-------------");
}
- 老寫法
- (void)groupQueue02 {
- 01.創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
- 02.創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
- 03.在該方法后面的異步任務(wù)會被納入到隊(duì)列組的監(jiān)聽范圍惨好,進(jìn)入群組
-
dispatch_group_enter
|dispatch_group_leave
必須要配對使用
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1--------------%@", [NSThread currentThread]);
// 離開群組
dispatch_group_leave(group);
});
dispatch_async(queue, ^{
NSLog(@"2--------------%@", [NSThread currentThread]);
// 離開群組
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"-----------------dispatch_group_notify-------------------");
});
}
- GCD 隊(duì)列組使用(需求,下載兩個(gè)圖片随闺,并將兩個(gè)圖片合并成一張圖片)
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
// 第一張圖
@property (nonatomic, strong) UIImage *image1;
// 第二張圖
@property (nonatomic, strong) UIImage *image2;
- (void) groupQueueAdhibition {
- 01.創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
- 02.獲取并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
- 03.下載圖片一日川,開子線程
dispatch_group_async(group, queue, ^{
// 3.1.獲取圖片的URL
NSURL *url = [NSURL URLWithString:@"http://scimg.jb51.net/allimg/151109/14-15110Z9535JH.jpg"];
// 3.2.下載圖片的二進(jìn)制數(shù)據(jù)
NSData *image1Data = [NSData dataWithContentsOfURL:url];
// 3.3.轉(zhuǎn)換圖片
self.image1 = [UIImage imageWithData:image1Data];
});
- 04.下載圖片二,開子線程
dispatch_group_async(group, queue, ^{
// 4.1.獲取圖片的URL
NSURL *url = [NSURL URLWithString:@"http://pic.qiantucdn.com/58pic/18/12/74/50B58PICWPb_1024.jpg"];
// 4.2.下載圖片的二進(jìn)制數(shù)據(jù)
NSData *image2Data = [NSData dataWithContentsOfURL:url];
// 4.3.轉(zhuǎn)換圖片
self.image2 = [UIImage imageWithData:image2Data];
});
- 05.合并圖片
dispatch_group_notify(group, queue, ^{
// 5.1 創(chuàng)建圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(300, 300));
// 5.2 畫圖1
[self.image1 drawInRect:CGRectMake(0, 0, 300, 150)];
self.image1 = nil;
// 5.3 畫圖2
[self.image2 drawInRect:CGRectMake(0, 150, 300, 150)];
self.image2 = nil;
// 5.4 根據(jù)上下文得到一張圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 5.5 關(guān)閉上下文
UIGraphicsEndImageContext();
// 5.6 刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
}
- GCD補(bǔ)充知識點(diǎn)
- (void) test02 {
// dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
// 封裝任務(wù)的方法不同(??:Block矩乐, ??:函數(shù))
/**
第一個(gè)參數(shù):隊(duì)列
第二個(gè)參數(shù):函數(shù)參數(shù)
第三個(gè)參數(shù):要調(diào)用的函數(shù)名稱
*/
// 會開子線程龄句,完成任務(wù)
dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
}
void task(void *param) {
NSLog(@"%s-----------%@", __func__, [NSThread currentThread]);
}