iOS - GCD 用法详解

2018-04-26  本文已影响29人  LYDeveloper

以前总是被并行、串行,同步、异步 队列搞得晕头转向,最近理顺了一遍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----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 0----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 0----<NSThread: 0x604000265580>{number = 5, name = (null)}
GCD[1489:125581] 222222--- 1----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 1----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 1----<NSThread: 0x604000265580>{number = 5, name = (null)}
GCD[1489:125581] 222222--- 2----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 2----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 2----<NSThread: 0x604000265580>{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-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222220-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238165] 111111-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222221-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238165] 111112-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222222-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 栅栏
GCD[2617:238164] 333330-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 333331-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 333332-----<NSThread: 0x60400027c1c0>{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 ----<NSThread: 0x600000275bc0>{number = 3, name = (null)}
2018-04-26 13:33:45.073933+0800 GCD[2735:248799] 0 ----<NSThread: 0x6000000634c0>{number = 1, name = main}
2018-04-26 13:33:45.073949+0800 GCD[2735:248895] 3 ----<NSThread: 0x604000263580>{number = 5, name = (null)}
2018-04-26 13:33:45.074026+0800 GCD[2735:248898] 1 ----<NSThread: 0x6040002634c0>{number = 4, name = (null)}
2018-04-26 13:33:47.077143+0800 GCD[2735:248799] 4 ----<NSThread: 0x6000000634c0>{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

上一篇下一篇

猜你喜欢

热点阅读