本文來自尚妝iOS團隊嘉文
發表於尚妝github部落格,歡迎訂閱!
GCD介紹
Grand Central Dispatch (GCD)是Apple開發的一個多核程式設計的解決方法
基於C語言,提供了非常多強大的函式
術語
同步 (Synchronous)
在當前執行緒中執行任務,不具備開啟新執行緒的能力
提交的任務在執行完成後才會返回
同步函式: dispatch_sync()非同步 (Asynchronous)
在新執行緒中執行任務,具備開啟新執行緒的能力
提交的任務立刻返回,在後臺佇列中執行
非同步函式: dispatch_async()序列 (Serial)
一個任務執行完畢後,再執行下一個任務
併發 (Concurrent)
多個任務同時執行(自動開啟多個執行緒),
只有在非同步函式下才有效
描述 | 說明 |
---|---|
queue | 佇列 |
main | 主佇列 |
global | 全域性佇列 |
dispatch_queue_t | 描述佇列 |
dispatch_block_t | 描述任務 |
dispatch_once_t | 描述一次性 |
dispatch_time_t | 描述時間 |
dispatch_group_t | 描述佇列組 |
dispatch_semaphore_t | 描述訊號量 |
函式 | 說明 |
---|---|
dispatch_sync() | 同步執行 |
dispatch_async() | 非同步執行 |
dispatch_after() | 延時執行 |
dispatch_once() | 一次性執行 |
dispatch_apply() | 提交佇列 |
dispatch_queue_create() | 建立佇列 |
dispatch_group_create() | 建立佇列組 |
dispatch_group_async() | 提交任務到佇列組 |
dispatch_group_enter() / dispatch_group_leave() | 將佇列組中的任務未執行完畢的任務數目加減1(兩個函式要配合使用) |
dispatch_group_notify() | 監聽佇列組執行完畢 |
dispatch_group_wait() | 設定等待時間(返回 0成功,1失敗) |
注意:
1.所有的執行都放到佇列中(queue),佇列的特點是FIFO(先提交的先執行)
2.必須在主執行緒訪問 UIKit 的類
3.併發佇列只在非同步函式下才有效
#基本使用
NSLog(@"當前執行緒: %@", [NSThread currentThread]);
//獲取主佇列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//獲取全域性併發佇列
dispatch_queue_t otherQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步函式(在當前執行緒中執行,不具備開啟新執行緒的能力)
dispatch_sync(otherQueue, ^{
NSLog(@"同步 %@", [NSThread currentThread]);
});
//非同步函式(在另一條執行緒中執行,具備開啟新執行緒的能力)
dispatch_async(otherQueue, ^{
NSLog(@"非同步 %@", [NSThread currentThread]);
});
//輸出: 當前執行緒: {number = 1, name = main}
//輸出: 同步 {number = 1, name = main}
//輸出: 非同步 {number = 3, name = (null)} 複製程式碼
延時執行 dispatch_after()
dispatch_after()延遲一段時間把一項任務提交到佇列中執行,返回之後就不能取消
常用來在在主佇列上延遲執行一項任務
NSLog(@"當前執行緒 %@", [NSThread currentThread]);
//GCD延時呼叫(主執行緒)(主佇列)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"GCD延時(主執行緒) %@", [NSThread currentThread]);
});
//GCD延時呼叫(其他執行緒)(全域性併發佇列)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"GCD延時(其他執行緒) %@", [NSThread currentThread]);
});
//輸出: 當前執行緒 {number = 1, name = main}
//輸出: GCD延時(主執行緒) {number = 1, name = main}
//輸出: GCD延時(其他執行緒) {number = 3, name = (null)} 複製程式碼
一次性執行 dispatch_once()
整個程式執行中,只會執行一次 (預設執行緒是安全的)
dispatch_once() 以執行緒安全的方式執行且僅執行其程式碼塊一次
for (NSInteger i = 0; i < 10; i++) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"GCD一次性執行(預設執行緒是安全的)");
});
}
//輸出: GCD一次性執行(預設執行緒是安全的)複製程式碼
//使用GCD初始化單例
+ (instancetype)sharedManager {
static PhotoManager *sharedPhotoManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPhotoManager = [[PhotoManager alloc] init];
});
return sharedPhotoManager;
}複製程式碼
提交 dispatch_apply()
把一項任務提交到佇列中多次執行,具體是並行執行還是序列執行由佇列本身決定
dispatch_apply不會立刻返回,在執行完畢後才會返回,是同步的呼叫。
佇列
任務1,任務2依次執行,所有任務都執行成功後回到主執行緒
(效率不高)
NSLog(@"當前執行緒 %@", [NSThread currentThread]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//全域性併發佇列
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任務1 %@", [NSThread currentThread]);
}
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任務2 %@", [NSThread currentThread]);
}
dispatch_async(dispatch_get_main_queue(), ^{
//主佇列
NSLog(@"主執行緒執行(重新整理UI) %@", [NSThread currentThread]);
});
});
//輸出: 當前執行緒 {number = 1, name = main}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務2 {number = 3, name = (null)}
//輸出: 任務2 {number = 3, name = (null)}
//輸出: 任務2 {number = 3, name = (null)}
//輸出: 任務2 {number = 3, name = (null)}
//輸出: 任務2 {number = 3, name = (null)}
//輸出: 任務2 {number = 3, name = (null)}
//輸出: 主執行緒(重新整理UI) {number = 1, name = main} 複製程式碼
佇列組
任務1,任務2同時執行,所有任務都執行成功後回到主執行緒
(效率高)
NSLog(@"當前執行緒 %@", [NSThread currentThread]);
//(1)建立一個佇列組
dispatch_group_t group= dispatch_group_create();
//(2)開啟任務1
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任務1 %@", [NSThread currentThread]);
}
});
//(3)開啟任務2
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任務2 %@", [NSThread currentThread]);
}
});
//(4)所有任務執行完畢,回到主執行緒
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"主執行緒(重新整理UI) %@", [NSThread currentThread]);
});
//輸出: 當前執行緒 {number = 1, name = main}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務2 {number = 4, name = (null)}
//輸出: 任務2 {number = 4, name = (null)}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務2 {number = 4, name = (null)}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務2 {number = 4, name = (null)}
//輸出: 任務2 {number = 4, name = (null)}
//輸出: 主執行緒(重新整理UI) {number = 1, name = main} 複製程式碼
#序列與併發
序列佇列
一個任務執行完畢後,再執行下一個任務
主佇列是GCD自帶的一種特殊的序列佇列,放在主佇列中的任務,都會放到主執行緒中執行
//(1)使用dispatch_queue_create函式建立序列佇列
//引數1: 佇列名稱
//引數2: 佇列屬性 (一般用NULL)
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
//(2)使用主佇列(跟主執行緒相關聯的佇列)
dispatch_queue_t serialMainQueue = dispatch_get_main_queue();複製程式碼
併發佇列
多個任務併發執行(自動開啟多個執行緒同時執行任務)
併發功能只有在非同步(dispatch_async)函式下才有效!!!
GCD預設已經提供了全域性的併發佇列,供整個應用使用,不需要手動建立
併發佇列優先順序 | 快捷值 | 優先順序 |
---|---|---|
DISPATCH_QUEUE_PRIORITY_HIGH | 2 | 高 |
DISPATCH_QUEUE_PRIORITY_DEFAULT | 0 | 中(預設) |
DISPATCH_QUEUE_PRIORITY_LOW | (-2) | 低 |
DISPATCH_QUEUE_PRIORITY_BACKGROUND | INT16_MIN | 後臺 |
//(1)使用dispatch_get_global_queue函式獲得全域性的併發佇列
//引數1: 優先順序
//引數2: 暫時無用引數 (傳0)
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);複製程式碼
非同步函式_併發佇列
(開啟新執行緒,併發執行任務)
NSLog(@"當前執行緒 %@", [NSThread currentThread]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任務1 %@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任務2 %@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任務3 %@", [NSThread currentThread]);
});
//輸出: 當前執行緒 {number = 1, name = main}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務3 {number = 4, name = (null)}
//輸出: 任務2 {number = 5, name = (null)} 複製程式碼
非同步函式_序列佇列
(開啟新執行緒,序列執行任務)
NSLog(@"當前執行緒 %@", [NSThread currentThread]);
//建立序列佇列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
dispatch_async(serialQueue, ^{
NSLog(@"任務1 %@", [NSThread currentThread]);
});
dispatch_async(serialQueue, ^{
NSLog(@"任務2 %@", [NSThread currentThread]);
});
dispatch_async(serialQueue, ^{
NSLog(@"任務3 %@", [NSThread currentThread]);
});
//輸出: 當前執行緒 {number = 1, name = main}
//輸出: 任務1 {number = 3, name = (null)}
//輸出: 任務2 {number = 3, name = (null)}
//輸出: 任務3 {number = 3, name = (null)} 複製程式碼
同步函式_併發佇列
(不會開啟新執行緒,併發執行任務失效!)
NSLog(@"當前執行緒 %@", [NSThread currentThread]);
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務1 %@", [NSThread currentThread]);
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務2 %@", [NSThread currentThread]);
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務3 %@", [NSThread currentThread]);
});
//輸出: 當前執行緒 {number = 1, name = main}
//輸出: 任務1 {number = 1, name = main}
//輸出: 任務2 {number = 1, name = main}
//輸出: 任務3 {number = 1, name = main} 複製程式碼
同步函式_序列佇列
(不會開啟新執行緒,序列執行任務)
NSLog(@"當前執行緒 %@", [NSThread currentThread]);
//建立序列佇列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
dispatch_sync(serialQueue, ^{
NSLog(@"任務1 %@", [NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"任務2 %@", [NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"任務3 %@", [NSThread currentThread]);
});
//輸出: 當前執行緒 {number = 1, name = main}
//輸出: 任務1 {number = 1, name = main}
//輸出: 任務2 {number = 1, name = main}
//輸出: 任務3 {number = 1, name = main} 複製程式碼