以前总是被并行、串行,同步、异步 队列搞得晕头转向,最近理顺了一遍GCD的用法,再也不迷路了。
一、Dispatch Queue 是什么?
执行处理的等待队列,简单点说就是:要做一件事情先给开条路给做好准备,做事情的时候直接处理事情。
Dispatch Queue 按照追加的顺序(先进先出 FIFO)执行处理。
二、 Dispatch Queue 队列种类
1、Serial Dispatch Queue 等待现在执行中处理结束 等待执行 (串行)
2、Concurent Dispatch Queue 不等待现在执行中处理结束 立即执行 (并行)
三 、创建一个 Serial Dispatch Queue 队列,顺序执行任务。
NSLog(@"begin");
// Serila Disapatch Queue 串行队列主要用在多个线程同事更新相同资源导致数据竞争时。
// 第一个参数是进程的标识符 Apple 推荐写法为 使用应用程序ID这种逆序全程域名
// DISPATCH_QUEUE_SERIAL 表示队列类型为 串行队列 等待上个任务执行完成 再往下执行。
dispatch_queue_t queue = dispatch_queue_create("queueSerial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_async(queue, ^{
NSLog(@"3");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
NSLog(@"end");
执行顺序为:
2018-04-26 10:45:33.304185+0800 GCD[1118:74823] begin
2018-04-26 10:45:33.304396+0800 GCD[1118:74823] end
2018-04-26 10:45:33.304408+0800 GCD[1118:74869] 1
2018-04-26 10:45:33.304573+0800 GCD[1118:74869] 2
2018-04-26 10:45:33.304710+0800 GCD[1118:74869] 3
2018-04-26 10:45:33.305090+0800 GCD[1118:74869] 4
结果很明显: 开始后新建了一个子线程queue, 主线程和子线程同时往下执行,主线程执行NSLog 输出@“end”,子线程按照顺序执行任务,输出 1 2 3 4 。
四、创建一个 Concurrent Dispatch Queue 队列,并发执行任务。
Concurrent Dispatch Queue 会立即执行1 ,不等1执行完 就开始执行2 ,不等2执行完就开始执行3,不等3执行完再回到
NSLog(@"begin");
// 并行 第一个参数 是线程标识符 第二个参数表示创建的是并行队列 立即执行
dispatch_queue_t queue =dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"111111--- %d----%@", i, [NSThread currentThread]);
};
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"222222--- %d----%@", i, [NSThread currentThread]);
};
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"333333--- %d----%@", i, [NSThread currentThread]);
};
});
NSLog(@"end");
结果为:
GCD[1489:125490] begin
GCD[1489:125490] end
GCD[1489:125581] 222222--- 0----{number = 4, name = (null)}
GCD[1489:125580] 111111--- 0----{number = 3, name = (null)}
GCD[1489:125578] 333333--- 0----{number = 5, name = (null)}
GCD[1489:125581] 222222--- 1----{number = 4, name = (null)}
GCD[1489:125580] 111111--- 1----{number = 3, name = (null)}
GCD[1489:125578] 333333--- 1----{number = 5, name = (null)}
GCD[1489:125581] 222222--- 2----{number = 4, name = (null)}
GCD[1489:125580] 111111--- 2----{number = 3, name = (null)}
GCD[1489:125578] 333333--- 2----{number = 5, name = (null)}
Concurrent Dispatch Queue 急性子 不管你有没有做完前边的事情,我反正要立即执行,会开启多个线程完成这个事情。
这是最基本的两种用法。
刚刚是自己创建Queue ,其实我们也可以使用系统的Queue,
五、系统Serial (串行)队列 dispatch_get_main_queue() 用法同三
/**
系统
主队列
是Serial 类型
*/
dispatch_queue_t mainQueue = dispatch_get_main_queue();
六、系统Concurrent(并行) 队列 dispatch_get_global_queue(0, 0) 用法同四
/**
系统
全局队列
是Concurrent 类型
第一个参数是优先级
DISPATCH_QUEUE_PRIORITY_HIGH 最高优先
DISPATCH_QUEUE_PRIORITY_DEFAULT 默认优先级 0
DISPATCH_QUEUE_PRIORITY_LOW 低
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台
*/
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
还有一些其他函数例如 :
七、栅栏:dispatch_barrier_async()<用来控制队列的执行先完成barrier 前的任务在完成barrier后的任务>
// 栅栏函数不能在 全局主线程中执行
// 栅栏函数不能在 全局主线程中执行
dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"11111%d-----%@", i,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"22222%d-----%@", i,[NSThread currentThread]);
}
});
dispatch_barrier_async(queue, ^{
NSLog(@"栅栏");
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"33333%d-----%@", i,[NSThread currentThread]);
}
});
输出结果为:
GCD[2617:238165] 111110-----{number = 3, name = (null)}
GCD[2617:238164] 222220-----{number = 4, name = (null)}
GCD[2617:238165] 111111-----{number = 3, name = (null)}
GCD[2617:238164] 222221-----{number = 4, name = (null)}
GCD[2617:238165] 111112-----{number = 3, name = (null)}
GCD[2617:238164] 222222-----{number = 4, name = (null)}
GCD[2617:238164] 栅栏
GCD[2617:238164] 333330-----{number = 4, name = (null)}
GCD[2617:238164] 333331-----{number = 4, name = (null)}
GCD[2617:238164] 333332-----{number = 4, name = (null)}
八、稍等:dispatch_after() <在子线程中过一段时间执行某个任务>
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"过2s后执行");
});
九、快速迭代:dispatch_apply() Demo 是from文件内容转移到to文件
// 会阻塞主线程进行
NSLog(@"begin");
NSString *from = @"/Users/youName/Desktop/from";
NSString *to = @"/Users/youName/Desktop/to";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *fileArray = [fileManager subpathsAtPath:from];
/**
第一个参数 :迭代次数
第二个参数 :所在的queue
第三个参数 :执行的任务
*/
dispatch_apply([fileArray count], dispatch_queue_create("apply", DISPATCH_QUEUE_CONCURRENT), ^(size_t index) {
sleep(2);
NSLog(@"%zd ----%@",index, [NSThread currentThread]);
NSString *fromPath = [from stringByAppendingPathComponent:fileArray[index]];
NSString *toPath = [to stringByAppendingPathComponent:fileArray[index]];
[fileManager moveItemAtPath:fromPath toPath:toPath error:nil];
});
NSLog(@"end");
输出结果为:可以看到apply会阻塞主线程进行。
2018-04-26 13:33:43.071251+0800 GCD[2735:248799] begin
2018-04-26 13:33:45.073933+0800 GCD[2735:248896] 2 ----{number = 3, name = (null)}
2018-04-26 13:33:45.073933+0800 GCD[2735:248799] 0 ----{number = 1, name = main}
2018-04-26 13:33:45.073949+0800 GCD[2735:248895] 3 ----{number = 5, name = (null)}
2018-04-26 13:33:45.074026+0800 GCD[2735:248898] 1 ----{number = 4, name = (null)}
2018-04-26 13:33:47.077143+0800 GCD[2735:248799] 4 ----{number = 1, name = main}
2018-04-26 13:33:47.078235+0800 GCD[2735:248799] end
十、队列组 dispatch_group_t group = dispatch_group_create();<保证所有的任务都已经完成最后在执行某个任务>
// demo 是两张图片下载完成后再合成一张图片
// 队列组 是保证任务都已经完成后在执行某个任务。
dispatch_group_t group = dispatch_group_create(); // 创建队列组
__block UIImage *image1 = [[UIImage alloc] init];
__block UIImage *image2 = [[UIImage alloc] init];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:imageView];
NSLog(@"begin");
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSString *imagePath =@"http://h.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ecf70575602a9759ee3c6ddb99.jpg";
image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagePath]]];
NSLog(@"任务1完成");
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSString *imagePath =@"http://d.hiphotos.baidu.com/image/pic/item/8435e5dde71190ef3ddc94b7c21b9d16fdfa60b6.jpg";
image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagePath]]];
NSLog(@"任务2完成");
});
// 所有任务执行完成后才会执行这个任务。
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有任务执行完毕开始最后合成图片操作");
// 拼接两张图片
// 开启图形上下文
UIGraphicsBeginImageContext(CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height));
// 画1
[image1 drawInRect:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width * 0.4)];
// 画2
[image2 drawInRect:CGRectMake(0, [UIScreen mainScreen].bounds.size.height *0.3, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height * 0.7)];
// 得到绘制好的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
// 回到主线程刷新UI
NSLog(@"合成完毕");
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
NSLog(@"回到主线程刷新UI");
});
});
NSLog(@"end");
输出结果为:可以看到group会等任务都完成后再做最后的操作。
2018-04-26 13:49:46.539181+0800 GCD[2910:273617] begin
2018-04-26 13:49:46.539414+0800 GCD[2910:273617] end
2018-04-26 13:49:48.905053+0800 GCD[2910:273683] 任务2完成
2018-04-26 13:49:48.905053+0800 GCD[2910:273679] 任务1完成
2018-04-26 13:49:48.905325+0800 GCD[2910:273617] 所有任务执行完毕开始最后合成图片操作
2018-04-26 13:49:48.941813+0800 GCD[2910:273617] 合成完毕
2018-04-26 13:49:48.942956+0800 GCD[2910:273617] 回到主线程刷新UI
十一、单例 dispatch_once()
// 在touchBegin 调用此方法
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只会执行一次 适合全局单例对象使用");
});
NSLog(@"别点了,就会执行一次");
输出结果为:
2018-04-26 13:53:42.167590+0800 GCD[2974:280429] 只会执行一次 适合全局单例对象使用
2018-04-26 13:53:42.167835+0800 GCD[2974:280429] 别点了,就会执行一次
2018-04-26 13:53:42.523634+0800 GCD[2974:280429] 别点了,就会执行一次
2018-04-26 13:53:42.685084+0800 GCD[2974:280429] 别点了,就会执行一次
还有一些其他的用用法我还没掌握,欢迎各位大牛批评指正。
Demo 地址:https://github.com/MYLILUYANG/GCD-
更加详细总结: http://www.cocoachina.com/ios/20180313/22573.html