1、GCD簡介
- 全稱是
Grand Central Dispatch
; - 純 C 語言,提供了非常多強大的函數;
- GCD是非常高效的多線程開發方式,它并不是Cocoa框架的一部分
1.1 GCD優勢
- GCD 是蘋果公司為多核的并行運算提出的解決方案;
- GCD 會自動利用更多的CPU內核(比如雙核、四核)
- GCD 會自動管理線程的生命周期(創建線程、調度任務、銷毀線程)
總結:將任務添加到隊列,并且指定執行任務的函數。
1.2 GCD函數
- 同步函數
- 通過
dispatch_sync(queue , {})
獲取; -
必須等待當前語句執行完畢
,才會執行下一條語句; -
不會開啟其他線程
,就在當前線程中完成任務;
- 通過
- 異步函數
- 通過
dispatch_async(queue , {})
獲取; -
不用等待當前語句執行完畢
,就可以執行下一條語句 -
會開啟線程
,異步就是多線程的代名詞;
- 通過
1.3 GCD隊列
- 主隊列
- 通過
dispatch_get_main_queue()
獲取; - 專?用來在
主線程
上調度任務的串行隊列;
- 通過
- 全局并發隊列
- 為了方便程序員的使用,蘋果提供了全局隊列
dispatch_get_global_queue(0, 0)
-
全局隊列是并發隊列
,包含四個優先級;
- 為了方便程序員的使用,蘋果提供了全局隊列
- 自定義隊列
- 通過
dispatch_queue_create("Lable", NULL);
獲取 - 根據參數不同可以獲取
串行隊列
、并發隊列
;
- 通過
1.3.1 串行隊列
1.3.2 并發隊列
2、GCD的使用
2.1 創建
- 同步函數
dispatch_sync(dispatch_get_main_queue();, ^{ });
- 異步函數
dispatch_async(dispatch_get_main_queue();, ^{ });
- 主隊列
dispatch_get_main_queue();
- 全局并發隊列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//全局并發隊列的優先級
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高優先級
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(中)優先級
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低優先級
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺優先級
- 自定義隊列
// 串行隊列
dispatch_queue_create("HRTest", DISPATCH_QUEUE_SERIAL);
// 并發隊列
dispatch_queue_create("HRTest", DISPATCH_QUEUE_CONCURRENT);
2.2 串行隊列同步函數
-(void)demo{
dispatch_queue_t queue = dispatch_queue_create("HRTest", NULL);
dispatch_sync(queue, ^{
NSLog(@"2");
});
}
-
不會開啟新的線程
,在當前主線程
下進行任務消耗;
這種搭配下很容易出現死鎖情況
-(void)demo{
dispatch_queue_t queue = dispatch_queue_create("HRTest", NULL);
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"1");
});
NSLog(@"2");
});
}
-
執行結果看起來像:1 2,但事實會出現死鎖;
- 任務一執行依賴任務三,任務三依賴任務二,任務二又依賴任務一;形成一個循環沒有出口;
2.3 串行隊列異步函數
-(void)demo{
NSLog(@"%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("HRTest", NULL);
for (int i = 0; i<3; i++) {
dispatch_async(queue, ^{
NSLog(@"1- %@",[NSThread currentThread]);
});
}
for (int i = 0; i<3; i++) {
dispatch_async(queue, ^{
NSLog(@"2- %@",[NSThread currentThread]);
});
}
for (int i = 0; i<3; i++) {
dispatch_async(queue, ^{
NSLog(@"3- %@",[NSThread currentThread]);
});
}
}
輸出:
- 異步函數串行隊列導致
只會開啟一條新的線程
;
這種搭配下也會出現死鎖情況
-(void)demo{
NSLog(@"%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("HRTest", NULL);
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
});
}
- 這種情況下也會產生死鎖,任務二(同步函數)和任務三(同步函數需要執行的block)相互等待;
總體來說涉及到串行隊列的嵌套就容易出現死鎖,使用時一定要注意;串行隊列里添加同步任務隊列必定會出現死鎖
;
2.3 并發隊列同步函數
-(void)demo{
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
// 耗時
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"3");
});
NSLog(@"4");
});
}
輸出:
- 雖然是并發隊列但是因為是同步函數,所以
不會開啟新的線程
,在當前主線程
下進行任務消耗;
2.4 并發函數異步隊列
-(void)demo{
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
// 耗時
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"2");
});
NSLog(@"3");
});
}
輸出:
-
會產生多條線程
;
2.5 GCD線程間通信
// 異步
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 耗時操作放在這里
[NSThread sleepForTimeInterval:2];
// 回到主線程處理UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
2.6 GCD延時執行
-(void)demo{
NSLog(@"%@",[NSDate date]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSDate date]);
});
}
輸出:
2.7 GCD柵欄
dispatch_queue_t queue = dispatch_queue_create("HRTest", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue1, ^{
NSLog(@"1");
});
dispatch_async(queue1, ^{
NSLog(@"2");
});
dispatch_barrier_async(queue1, ^{
NSLog(@"3");
});
dispatch_async(queue1, ^{
NSLog(@"4");
});
- 1 2 一定在3前面執行,4一定在3后面執行;
2.8 GCD隊列組
隊列組有下面幾個特點:
- 所有的任務會并發的執行(不按序)。
- 所有的異步函數都添加到隊列中,然后再納入隊列組的監聽范圍。
- 使用dispatch_group_notify函數,來監聽上面的任務是否完成,如果完成, 就會調用這個方法。
-(void)demo{
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, ^{
NSLog(@"下載1開始");
[NSThread sleepForTimeInterval:2];
NSLog(@"下載1結束");
});
dispatch_group_async(group, queue, ^{
NSLog(@"下載2開始");
[NSThread sleepForTimeInterval:2];
NSLog(@"下載2結束");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"下載全部結束");
//在主線程顯示
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
}
2.9 GCD信號量
// 創建一個信號,value:信號量
dispatch_semaphore_create(<#long value#>)
// 使某個信號的信號量+1
dispatch_semaphore_signal(<#dispatch_semaphore_t dsema#>)
// 某個信號進行等待或等待降低信號量 timeout:等待時間,永遠等待為 DISPATCH_TIME_FOREVER
dispatch_semaphore_wait(<#dispatch_semaphore_t dsema#>, <#dispatch_time_t timeout#>)
- 正常的使用順序是
先降低然后再提高
,這兩個函數通常成對使用`。 -
信號量代表可以進入的線程數
,信號量為0表示當前線程堵塞
;
2.10 單例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});