美文网首页面试专题
iOS GCD的详细介绍

iOS GCD的详细介绍

作者: MiniCoder | 来源:发表于2020-03-12 16:31 被阅读0次

    什么是GCD:
    GCD的全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”;
    纯C语言,提供了非常多强大的函数;
    GCD是苹果公司为多核的并行运算提出的解决方案;
    GCD会自动利用更多的CPU内核(比如双核、四核);
    GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程);
    程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码;
    GCD中的两大核心
    任务:执行什么操作
    队列:用来存放任务
    GCD的使用步骤
    定制任务:确定想做的事情
    将任务添加到队列中:<1>GCD会自动将队列中的任务取出,放到对应的线程中执行。<2>任务的取出遵循队列的FIFO原则,及先进先出,后进后出。
    GCD中两个用来执行任务的常用函数

    同步的方法执行任务,同步函数

    dispatch_sync(dispatch_queue_t  _Nonnull queue, dispatch_block_t block);
    

    异步的方法执行任务,异步函数

    dispatch_async(dispatch_queue_t  _Nonnull queue, dispatch_block_t block);
    

    block---任务

    同步和异步的区别:
    同步:只能在当前线程中执行任务,不具备开启新线程的能力
    异步:可以在新的线程中执行任务,具备开启新线程的能力(具备但是不一定非要开)
    -GCD的队列可分为两种
    并发队列:<1>可以让多个任务并发(同时)执行(自动开启对个线程同时执行任务,只要第一个任务取出来之后,不在等待执行完毕就可以接着取第二个任务) <2>并发功能只能在异步函数下才有效
    串行队列:让任务一个接着一个执行(一个任务执行完毕后,再执行下一个任务)
    GCD的几种组合使用

    • 异步函数+并发队列
    /**
     异步函数+并发队列:会开启多条线程,所有的任务并发执行
     //注意:开几条线程并不是由任务数量决定的,是由GCD内部自动决定的
     //输出结果(number=1的才是主线程,其他的编号是由于Xcode添加的)
     2019-02-21 11:16:43.478993+0800 SmallProgram[3127:62594] 3---当前线程<NSThread: 0x600001719700>{number = 3, name = (null)}
     2019-02-21 11:16:43.479036+0800 SmallProgram[3127:62593] 2---当前线程<NSThread: 0x6000017e1840>{number = 8, name = (null)}
     2019-02-21 11:16:43.479042+0800 SmallProgram[3127:62611] 4---当前线程<NSThread: 0x6000017de5c0>{number = 9, name = (null)}
     2019-02-21 11:16:43.479061+0800 SmallProgram[3127:62610] 1---当前线程<NSThread: 0x6000017fbb00>{number = 6, name = (null)}
     */
    -(void)asyncConcurrent{
        //01--创建队列
        /**
         参数说明
         第一个参数:C语言的字符串,给队列起个名字,切记不要添加@,因为是C语言的
         第二个参数:类型  DISPATCH_QUEUE_CONCURRENT 并发队列
                       DISPATCH_QUEUE_SERIAL 串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_CONCURRENT);
        //02--封装任务,把任务添加到队列
        dispatch_async(queue, ^{
            NSLog(@"1---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"4---当前线程%@",[NSThread currentThread]);
        });
        
    }
    
    • 异步函数+串行队列
    /**
     异步函数+串行队列:会开启一条子线程,所有的任务在该子线程中串行执行
     //输出结果(number=1的才是主线程,其他的编号是由于Xcode添加的)
     2019-02-21 11:21:50.500116+0800 SmallProgram[3205:66377] 1---当前线程<NSThread: 0x600000f30740>{number = 7, name = (null)}
     2019-02-21 11:21:50.500287+0800 SmallProgram[3205:66377] 2---当前线程<NSThread: 0x600000f30740>{number = 7, name = (null)}
     2019-02-21 11:21:50.500399+0800 SmallProgram[3205:66377] 3---当前线程<NSThread: 0x600000f30740>{number = 7, name = (null)}
     2019-02-21 11:21:50.500515+0800 SmallProgram[3205:66377] 4---当前线程<NSThread: 0x600000f30740>{number = 7, name = (null)}
     */
    -(void)asyncSerial{
        //01--创建队列
        /**
         参数说明
         第一个参数:C语言的字符串,给队列起个名字,切记不要添加@,因为是C语言的
         第二个参数:类型  DISPATCH_QUEUE_CONCURRENT 并发队列
         DISPATCH_QUEUE_SERIAL 串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL);
        //02--封装任务,把任务添加到队列
        dispatch_async(queue, ^{
            NSLog(@"1---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"4---当前线程%@",[NSThread currentThread]);
        });
        
    }
    

    同步函数+并发队列

    /**
     同步函数+并发队列:(同步函数不具备开始线程)不会开启子线程,所有的任务在当前线程中串行执行
     //输出结果(number=1的才是主线程,其他的编号是由于Xcode添加的)
     2019-02-21 11:30:45.546540+0800 SmallProgram[3349:71630] 1---当前线程<NSThread: 0x600003dd6940>{number = 1, name = main}
     2019-02-21 11:30:45.546734+0800 SmallProgram[3349:71630] 2---当前线程<NSThread: 0x600003dd6940>{number = 1, name = main}
     2019-02-21 11:30:45.546859+0800 SmallProgram[3349:71630] 3---当前线程<NSThread: 0x600003dd6940>{number = 1, name = main}
     2019-02-21 11:30:45.546976+0800 SmallProgram[3349:71630] 4---当前线程<NSThread: 0x600003dd6940>{number = 1, name = main}
     */
    -(void)syncConcurrent{
        //01--创建队列
        /**
         参数说明
         第一个参数:C语言的字符串,给队列起个名字,切记不要添加@,因为是C语言的
         第二个参数:类型  DISPATCH_QUEUE_CONCURRENT 并发队列
         DISPATCH_QUEUE_SERIAL 串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_CONCURRENT);
        //02--封装任务,把任务添加到队列
        dispatch_sync(queue, ^{
            NSLog(@"1---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"4---当前线程%@",[NSThread currentThread]);
        });
        
    }
    
    • 同步函数+串行队列
    /**
     同步函数+串行队列:(同步函数不具备开始线程)不会开启子线程,所有的任务在当前线程中串行执行
     //输出结果(number=1的才是主线程,其他的编号是由于Xcode添加的)
     2019-02-21 11:33:36.127713+0800 SmallProgram[3420:73690] 1---当前线程<NSThread: 0x600003c00d80>{number = 1, name = main}
     2019-02-21 11:33:36.127911+0800 SmallProgram[3420:73690] 2---当前线程<NSThread: 0x600003c00d80>{number = 1, name = main}
     2019-02-21 11:33:36.128021+0800 SmallProgram[3420:73690] 3---当前线程<NSThread: 0x600003c00d80>{number = 1, name = main}
     2019-02-21 11:33:36.128148+0800 SmallProgram[3420:73690] 4---当前线程<NSThread: 0x600003c00d80>{number = 1, name = main
     */
    -(void)syncSerial{
        //01--创建队列
        /**
         参数说明
         第一个参数:C语言的字符串,给队列起个名字,切记不要添加@,因为是C语言的
         第二个参数:类型  DISPATCH_QUEUE_CONCURRENT 并发队列
         DISPATCH_QUEUE_SERIAL 串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL);
        
        //02--封装任务,把任务添加到队列
        dispatch_sync(queue, ^{
            NSLog(@"1---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"4---当前线程%@",[NSThread currentThread]);
        });
        
    }
    
    • 异步函数+主队列
    /**
     异步函数+主队列:不会开线程,所有任务都在主线程中串行执行
     //输出结果
     2019-02-21 13:37:50.406381+0800 SmallProgram[5481:145502] 测试1
     2019-02-21 13:37:50.406381+0800 SmallProgram[5481:145502] 测试2
     2019-02-21 13:37:50.406381+0800 SmallProgram[5100:128550] 1---当前线程<NSThread: 0x60000336d7c0>{number = 1, name = main}
     2019-02-21 13:37:50.406613+0800 SmallProgram[5100:128550] 2---当前线程<NSThread: 0x60000336d7c0>{number = 1, name = main}
     2019-02-21 13:37:50.406778+0800 SmallProgram[5100:128550] 3---当前线程<NSThread: 0x60000336d7c0>{number = 1, name = main}
     2019-02-21 13:37:50.406936+0800 SmallProgram[5100:128550] 4---当前线程<NSThread: 0x60000336d7c0>{number = 1, name = main}
     */
    -(void)asyncMain{
        //01--创建队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        NSLog(@"测试1");
        //02--封装任务,把任务添加到队列
        dispatch_async(queue, ^{
            NSLog(@"1---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3---当前线程%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"4---当前线程%@",[NSThread currentThread]);
        });
        NSLog(@"测试2");
    } 
    
    • 同步函数+主队列(会崩溃):
    /**
     同步函数+主队列:
     情况一:在主线程调用syncMain
     分析:不会开线程,这段代码会死锁崩溃(队列为主队列,所以只能在主线程中执行,但是当前主线程在执行syncMain方法,被占用着,因为当前函数还未运行完),此时思考下异步+主队列为啥不死锁?这个面试官很爱问的。答案是:因为是异步的,所以程序可以先执行完一个任务在执行另外一个任务。
     解释:当主队列中有任务的时候,主队列就会安排主线程来来执行该任务,但是在调度之前会先检查主线程的状态(是否在忙),如果主线程当前在忙,那么暂停调度,值到主线程空闲为止。
     情况二:在子线程调用syncMain
     分析:因为在走dispatch_sync方法的时候主线程在空闲状态。
     输出结果:
     2019-02-21 13:55:13.366052+0800 SmallProgram[5384:140751] 1---当前线程<NSThread: 0x60000124d000>{number = 1, name = main}
     2019-02-21 13:55:13.366318+0800 SmallProgram[5384:140751] 2---当前线程<NSThread: 0x60000124d000>{number = 1, name = main}
     2019-02-21 13:55:13.366544+0800 SmallProgram[5384:140751] 3---当前线程<NSThread: 0x60000124d000>{number = 1, name = main}
     2019-02-21 13:55:13.366828+0800 SmallProgram[5384:140751] 4---当前线程<NSThread: 0x60000124d000>{number = 1, name = main}
     */
    -(void)syncMain{
        //01--创建队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        //02--封装任务,把任务添加到队列
        dispatch_sync(queue, ^{
            NSLog(@"1---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3---当前线程%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"4---当前线程%@",[NSThread currentThread]);
        });
        
    }
    
    • GCD的日常几点使用
      GCD的一次性代码:
       -(void)once{
        //整个程序运行过程中只会执行一次+本身是线程安全的
        //应用:单例模式
        //内部原理:通过判断onceToken的值来决定是否执行block中的任务,只有在第一次为0,其它都为-1
        static dispatch_once_t onceToken;
        NSLog(@"%zd",onceToken);
        dispatch_once(&onceToken, ^{
            //任务
        });
        
    }
    
    • GCD的延迟执行:
    -(void)delay{
        
        /**
         GCD的延迟执行
         @param when#> 设置时间(CGD中的时间单位为纳秒) description#>
         @param queue#> 队列(决定block块红的任务在哪个线程中执行,如果主队列就在主线程,否则在子线程) description#>
         @param void 执行的任务
         原理:先等2秒,然后在吧任务提交到队列,如果先提交到队列,那么任务不好控制,并且很好队列的资源
         */
        NSLog(@"hahaha");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"hahaha%@",[NSThread currentThread]);
        });
    }
    

    GCD的快速迭代:(小应用:将一个文件夹下多张图片剪切到另外一个文件夹下)

    //快速迭代(遍历)
    -(void)apply{
        //在当前线程中串行执行----0---<NSThread: 0x600003b9d680>{number = 1, name = main}
        for(int i=0;i<10;i++){
            NSLog(@"%d---%@",i,[NSThread currentThread]);
        }
        /**
         //快速迭代--会开启多条子线程和主线程一起并发的执行任务
         iterations#> 遍历的次数
         queue#> 队列 --如果是主队列则死锁,如果是串行队列则跟for循环一样
         size_t 索引
         */
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_apply(10, queue, ^(size_t i) {
            NSLog(@"%zd---%@",i,[NSThread currentThread]);
        });
    }
    
    • GCD的栅栏函数()
    -(void)barrier{
        //栅栏函数
        //需求:有一个新任务打印00000的任务,要求在1 2执行完之后执行,要保证该任务执行完之后才能执行后面的3 4任务。
        //讲解:栅栏前面的任务并发执行,后面的任务也是并发执行,当前面的任务执行完之后执行栅栏函数中的任务,等该任务执行完毕后在执行后面的任务。
        //警告:不能使用全局并发队列(dispatch_get_global_queue(0, 0)),否则不能拦截
        //01获得队列
        //
        dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
        //02封装任务,并且添加到队列
        dispatch_async(queue, ^{
            NSLog(@"01---%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"02---%@",[NSThread currentThread]);
        });
        //栅栏函数
        dispatch_barrier_async(queue, ^{
            NSLog(@"00000");
        });
        dispatch_async(queue, ^{
            NSLog(@"03---%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"04---%@",[NSThread currentThread]);
        });
        
    }
    

    GCD的队列组(控制多条队列中任务都完成在执行新的,可用在下载多张小图片,然后都下载完成后拼接起来)

    //GCD的队列组
    -(void)group{
        //需求:有5个任务,在多个队列的子线程中并发执行,添加打印00000的任务,必须在所有任务完成后在执行
        //增加需求:拦截多个队列中的任务
        
        //01 创建队列组
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_queue_t queue01 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
        dispatch_queue_t queue02 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_group_async(group, queue01, ^{
            NSLog(@"01---%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, queue01, ^{
            NSLog(@"02---%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, queue01, ^{
            NSLog(@"03---%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, queue02, ^{
            NSLog(@"04---%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, queue02, ^{
            NSLog(@"05---%@",[NSThread currentThread]);
        });
        //03 拦截通知,当所有的任务都执行完毕然后打印00000
        //注意:通知中的第二个参数能控制执行最后的任务在子线程还是主线程
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"00000--%@",[NSThread currentThread]);
        });
        
    }
    

    总结:
    同步函数:
    +串行队列:不会开线程,所有任务在当前线程串行执行
    +并发队列:不会开线程,所有任务在当前线程串行执行
    +主队列:死锁
    异步函数:
    +串行队列:会开1条线程,所有任务在子线程中串行执行
    +并发队列:会开N条线程,所有任务在子线程中并发执(注意:线程的数量并不等于任务的数量)
    +主队列:不会开线程,所有任务在主线程串行执行

    相关文章

      网友评论

        本文标题:iOS GCD的详细介绍

        本文链接:https://www.haomeiwen.com/subject/uqzpjhtx.html