GCD介紹
Grand Central Dispatch (GCD)是Apple開發(fā)的一個多核編程的解決方法
基于C語言牌借,提供了非常多強(qiáng)大的函數(shù)
GCD和其他多線程技術(shù)方案,如NSThread,NSOperationQueue,NSInvocationOperation等相比,使用起來更加方便翰灾。
術(shù)語
同步 (Synchronous)
在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
提交的任務(wù)在執(zhí)行完成后才會返回
同步函數(shù): dispatch_sync()異步 (Asynchronous)
在新線程中執(zhí)行任務(wù)稚茅,具備開啟新線程的能力
提交的任務(wù)立刻返回纸淮,在后臺隊列中執(zhí)行
異步函數(shù): dispatch_async()串行 (Serial)
一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù)
并發(fā) (Concurrent)
多個任務(wù)同時執(zhí)行(自動開啟多個線程),
只有在異步函數(shù)下才有效
描述 | 說明 |
---|---|
queue | 隊列 |
main | 主隊列 |
global | 全局隊列 |
dispatch_queue_t | 描述隊列 |
dispatch_block_t | 描述任務(wù) |
dispatch_once_t | 描述一次性 |
dispatch_time_t | 描述時間 |
dispatch_group_t | 描述隊列組 |
dispatch_semaphore_t | 描述信號量 |
函數(shù) | 說明 |
---|---|
dispatch_sync() | 同步執(zhí)行 |
dispatch_async() | 異步執(zhí)行 |
dispatch_after() | 延時執(zhí)行 |
dispatch_once() | 一次性執(zhí)行 |
dispatch_apply() | 提交隊列 |
dispatch_queue_create() | 創(chuàng)建隊列 |
dispatch_group_create() | 創(chuàng)建隊列組 |
dispatch_group_async() | 提交任務(wù)到隊列組 |
dispatch_group_enter() / dispatch_group_leave() | 將隊列組中的任務(wù)未執(zhí)行完畢的任務(wù)數(shù)目加減1(兩個函數(shù)要配合使用) |
dispatch_group_notify() | 監(jiān)聽隊列組執(zhí)行完畢 |
dispatch_group_wait() | 設(shè)置等待時間(返回 0成功,1失敗) |
注意:
1.所有的執(zhí)行都放到隊列中(queue)峰锁,隊列的特點(diǎn)是FIFO(先提交的先執(zhí)行)
2.必須在主線程訪問 UIKit 的類
3.并發(fā)隊列只在異步函數(shù)下才有效
基本使用
NSLog(@"當(dāng)前線程: %@", [NSThread currentThread]);
//獲取主隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//獲取全局并發(fā)隊列
dispatch_queue_t otherQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步函數(shù)(在當(dāng)前線程中執(zhí)行,不具備開啟新線程的能力)
dispatch_sync(otherQueue, ^{
NSLog(@"同步 %@", [NSThread currentThread]);
});
//異步函數(shù)(在另一條線程中執(zhí)行,具備開啟新線程的能力)
dispatch_async(otherQueue, ^{
NSLog(@"異步 %@", [NSThread currentThread]);
});
//輸出: 當(dāng)前線程: <NSThread: 0x7f81bac06670>{number = 1, name = main}
//輸出: 同步 <NSThread: 0x7f81bac06670>{number = 1, name = main}
//輸出: 異步 <NSThread: 0x7f81bae35be0>{number = 3, name = (null)}
延時執(zhí)行 dispatch_after()
dispatch_after()延遲一段時間把一項任務(wù)提交到隊列中執(zhí)行萎馅,返回之后就不能取消
常用來在在主隊列上延遲執(zhí)行一項任務(wù)
NSLog(@"當(dāng)前線程 %@", [NSThread currentThread]);
//GCD延時調(diào)用(主線程)(主隊列)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"GCD延時(主線程) %@", [NSThread currentThread]);
});
//GCD延時調(diào)用(其他線程)(全局并發(fā)隊列)
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]);
});
//輸出: 當(dāng)前線程 <NSThread: 0x7f8cb9701990>{number = 1, name = main}
//輸出: GCD延時(主線程) <NSThread: 0x7f8cb9701990>{number = 1, name = main}
//輸出: GCD延時(其他線程) <NSThread: 0x7f8cb9513ee0>{number = 3, name = (null)}
一次性執(zhí)行 dispatch_once()
整個程序運(yùn)行中,只會執(zhí)行一次 (默認(rèn)線程是安全的)
dispatch_once() 以線程安全的方式執(zhí)行且僅執(zhí)行其代碼塊一次
for (NSInteger i = 0; i < 10; i++) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"GCD一次性執(zhí)行(默認(rèn)線程是安全的)");
});
}
//輸出: GCD一次性執(zhí)行(默認(rèn)線程是安全的)
//使用GCD初始化單例
+ (instancetype)sharedManager {
static PhotoManager *sharedPhotoManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPhotoManager = [[PhotoManager alloc] init];
});
return sharedPhotoManager;
}
提交 dispatch_apply()
把一項任務(wù)提交到隊列中多次執(zhí)行,具體是并行執(zhí)行還是串行執(zhí)行由隊列本身決定
dispatch_apply不會立刻返回虹蒋,在執(zhí)行完畢后才會返回糜芳,是同步的調(diào)用。
隊列
任務(wù)1,任務(wù)2依次執(zhí)行,所有任務(wù)都執(zhí)行成功后回到主線程
(效率不高)
NSLog(@"當(dāng)前線程 %@", [NSThread currentThread]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//全局并發(fā)隊列
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任務(wù)1 %@", [NSThread currentThread]);
}
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任務(wù)2 %@", [NSThread currentThread]);
}
dispatch_async(dispatch_get_main_queue(), ^{
//主隊列
NSLog(@"主線程執(zhí)行(刷新UI) %@", [NSThread currentThread]);
});
});
//輸出: 當(dāng)前線程 <NSThread: 0x7fa78040b8b0>{number = 1, name = main}
//輸出: 任務(wù)1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
//輸出: 主線程(刷新UI) <NSThread: 0x7f9da1705940>{number = 1, name = main}
隊列組
任務(wù)1,任務(wù)2同時執(zhí)行,所有任務(wù)都執(zhí)行成功后回到主線程
(效率高)
NSLog(@"當(dāng)前線程 %@", [NSThread currentThread]);
//(1)創(chuàng)建一個隊列組
dispatch_group_t group= dispatch_group_create();
//(2)開啟任務(wù)1
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任務(wù)1 %@", [NSThread currentThread]);
}
});
//(3)開啟任務(wù)2
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任務(wù)2 %@", [NSThread currentThread]);
}
});
//(4)所有任務(wù)執(zhí)行完畢,回到主線程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"主線程(刷新UI) %@", [NSThread currentThread]);
});
//輸出: 當(dāng)前線程 <NSThread: 0x7fd951704e70>{number = 1, name = main}
//輸出: 任務(wù)1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
//輸出: 任務(wù)1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
//輸出: 任務(wù)1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
//輸出: 任務(wù)1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
//輸出: 任務(wù)1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
//輸出: 主線程(刷新UI) <NSThread: 0x7ff65a406fb0>{number = 1, name = main}
串行與并發(fā)
串行隊列
一個任務(wù)執(zhí)行完畢后魄衅,再執(zhí)行下一個任務(wù)
主隊列是GCD自帶的一種特殊的串行隊列,放在主隊列中的任務(wù),都會放到主線程中執(zhí)行
//(1)使用dispatch_queue_create函數(shù)創(chuàng)建串行隊列
//參數(shù)1: 隊列名稱
//參數(shù)2: 隊列屬性 (一般用NULL)
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
//(2)使用主隊列(跟主線程相關(guān)聯(lián)的隊列)
dispatch_queue_t serialMainQueue = dispatch_get_main_queue();
并發(fā)隊列
多個任務(wù)并發(fā)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
<u>并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效!!!</u>
GCD默認(rèn)已經(jīng)提供了全局的并發(fā)隊列峭竣,供整個應(yīng)用使用,不需要手動創(chuàng)建
并發(fā)隊列優(yōu)先級 | 快捷值 | 優(yōu)先級 |
---|---|---|
DISPATCH_QUEUE_PRIORITY_HIGH | 2 | 高 |
DISPATCH_QUEUE_PRIORITY_DEFAULT | 0 | 中(默認(rèn)) |
DISPATCH_QUEUE_PRIORITY_LOW | (-2) | 低 |
DISPATCH_QUEUE_PRIORITY_BACKGROUND | INT16_MIN | 后臺 |
//(1)使用dispatch_get_global_queue函數(shù)獲得全局的并發(fā)隊列
//參數(shù)1: 優(yōu)先級
//參數(shù)2: 暫時無用參數(shù) (傳0)
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
異步函數(shù)_并發(fā)隊列
(開啟新線程,并發(fā)執(zhí)行任務(wù))
NSLog(@"當(dāng)前線程 %@", [NSThread currentThread]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任務(wù)1 %@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任務(wù)2 %@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任務(wù)3 %@", [NSThread currentThread]);
});
//輸出: 當(dāng)前線程 <NSThread: 0x7fbe83c067c0>{number = 1, name = main}
//輸出: 任務(wù)1 <NSThread: 0x7fb84160ed90>{number = 3, name = (null)}
//輸出: 任務(wù)3 <NSThread: 0x7fb841752940>{number = 4, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7fb8414167b0>{number = 5, name = (null)}
異步函數(shù)_串行隊列
(開啟新線程,串行執(zhí)行任務(wù))
NSLog(@"當(dāng)前線程 %@", [NSThread currentThread]);
//創(chuàng)建串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
dispatch_async(serialQueue, ^{
NSLog(@"任務(wù)1 %@", [NSThread currentThread]);
});
dispatch_async(serialQueue, ^{
NSLog(@"任務(wù)2 %@", [NSThread currentThread]);
});
dispatch_async(serialQueue, ^{
NSLog(@"任務(wù)3 %@", [NSThread currentThread]);
});
//輸出: 當(dāng)前線程 <NSThread: 0x7fc192e05380>{number = 1, name = main}
//輸出: 任務(wù)1 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
//輸出: 任務(wù)2 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
//輸出: 任務(wù)3 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
同步函數(shù)_并發(fā)隊列
(不會開啟新線程,并發(fā)執(zhí)行任務(wù)失效!)
NSLog(@"當(dāng)前線程 %@", [NSThread currentThread]);
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)1 %@", [NSThread currentThread]);
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)2 %@", [NSThread currentThread]);
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)3 %@", [NSThread currentThread]);
});
//輸出: 當(dāng)前線程 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
//輸出: 任務(wù)1 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
//輸出: 任務(wù)2 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
//輸出: 任務(wù)3 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
同步函數(shù)_串行隊列
(不會開啟新線程,串行執(zhí)行任務(wù))
NSLog(@"當(dāng)前線程 %@", [NSThread currentThread]);
//創(chuàng)建串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
dispatch_sync(serialQueue, ^{
NSLog(@"任務(wù)1 %@", [NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"任務(wù)2 %@", [NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"任務(wù)3 %@", [NSThread currentThread]);
});
//輸出: 當(dāng)前線程 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
//輸出: 任務(wù)1 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
//輸出: 任務(wù)2 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
//輸出: 任務(wù)3 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
多個網(wǎng)絡(luò)請求示例
- (void)exampleMoreNetwork{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t serialQueue = dispatch_queue_create("magic_gcd_group", DISPATCH_QUEUE_SERIAL);
// 網(wǎng)絡(luò)請求1
dispatch_group_enter(group);
dispatch_group_async(group, serialQueue, ^{
[[MagicNetworkManager shareManager] GET:@"網(wǎng)絡(luò)請求1" Parameters:nil Success:^(NSURLResponse *response, id responseObject) {
dispatch_group_leave(group);
} Failure:^(NSURLResponse *response, id error) {
dispatch_group_leave(group);
}];
});
// 網(wǎng)絡(luò)請求2
dispatch_group_enter(group);
dispatch_group_async(group, serialQueue, ^{
[[MagicNetworkManager shareManager] GET:@"網(wǎng)絡(luò)請求2" Parameters:nil Success:^(NSURLResponse *response, id responseObject) {
dispatch_group_leave(group);
} Failure:^(NSURLResponse *response, id error) {
dispatch_group_leave(group);
}];
});
// 所有網(wǎng)絡(luò)請求結(jié)束
dispatch_group_notify(group, serialQueue, ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
// 主線程刷新UI
});
});
});
}
多張圖片上傳示例
- (void)networkUploadWithURLString:(NSString *)URLString images:(NSMutableArray *)images {
NSMutableArray *result = [NSMutableArray array];
for (NSInteger i = 0; i < images.count; i++) {
[result addObject:[NSNull null]];
}
dispatch_group_t group = dispatch_group_create();
for (NSInteger i = 0; i < images.count; i++) {
dispatch_group_enter(group);
//1. 圖片存放地址
NSString *url = URLString;
//2. 圖片名稱自定義
NSString *imageName = [[NSDate date] formattedDateWithFormat:@"yyyyMMddHHmmss"];
NSString *fileName = [NSString stringWithFormat:@"%@_%ld.png",imageName,i];
//3. 圖片二進(jìn)制文件
NSData *imageData = UIImagePNGRepresentation(images[i]);
//NSLog(@"文件大小: %ld k", (long)(imageData.length / 1024));
//4. 發(fā)起網(wǎng)絡(luò)請求
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"application/json", @"text/html", nil];
[manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
[formData appendPartWithFileData:imageData name:@"file" fileName:fileName mimeType:@"image/png"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"第 %d 張圖片上傳成功: %@", (int)i + 1, responseObject);
// NSMutableArray 是線程不安全的晃虫,所以加個同步鎖
@synchronized (result) {
result[i] = responseObject;
}
dispatch_group_leave(group);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"第 %d 張圖片上傳失敗: %@", (int)i + 1, error);
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"上傳完成!");
for (id response in result) {
NSLog(@"%@", response);
}
});
}