美文网首页
GCD的简单使用

GCD的简单使用

作者: 3fbfd3c15df9 | 来源:发表于2017-10-22 22:19 被阅读15次

    GCD简单使用

    • GC使用就2个步骤
    1. 定制任务
    2. 将任务添加到队列中,gcd会自动将队列中的任务取出,放到对应的线程中执行,任务遵循队列:先进先出,后进后出 ,2个口 。。。。栈内存:先进后出,后进先出,一个口

    gcd2个用来执行任务的常用函数

    //异步 queue 队列 block 任务
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    //同步
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    //这一句代码做了2件事 ,1,封装任务 ,2,把任务丢到队列中
    
    

    gcd 队列分2大类。

    队列的作用 1:队列就是用来装任务的,并且安排队列的任务到那个线程中执行 , 封装任务 , 2:让任务到线程中执行

    1. 并发队列 2.串行队列

    并发队列
    1. 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    2. 并发功能只能在异步函数先才有效(dispatch_async)

    串行队列
    1.任务一个接一个的执行

    同步异步 的区别

    • 同步:只能在当前线程中执行任务,不具备开新线程的能力
    • 异步 :可以在新的线程中执行任务,具备开启新线程的能力

    术语之间的区别于影响,同步,异步,并发,串行

    同步函数(dispatch_sync)异步函数(dispatch_async)主要影响能否开启新的线程 , 同步函数,立刻马上执行。他不执行完就不能进行下一条

    同步函数:只能在当前线程中执行任务,执行完成后进行下一条任务,不具备开新线程的能力
    异步函数 :可以在新的线程中执行任务,具备开启新线程的能力
    

    串行队列和并发队列主要影响任务的执行方式

    并发队列:允许多个任务并发执行
    串行串行:一个任务完成才能执行下一个任务
    

    一些组合例子

    创建 异步函数 + 并发队列

    // 创建 异步函数 + 并发队列:次方法可以开多条线程,队列任务是异步(即并发)执行的
    -(void)asyncConcurrent
    {
        /*
         一 ,创建队列
         一个队列可以添加多个任务
         1,参数一:C语言字符串,随便给他命名
         2,参数二:队列的类型  DISPATCH_QUEUE_CONCURRENT 并发队列
                            DISPATCH_QUEUE_SERIAL 串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("xc.download", DISPATCH_QUEUE_CONCURRENT);
        
        //二,封装任务
        /*
            参数1: 队列
            参数2: 要执行的任务
         */
        dispatch_async(queue, ^{
            NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
        });
    }
    

    异步函数 + 串行队列

    // 异步函数 + 串行队列 ;是可以开线程的,只开一条线程,是串行执行的
    -(void)asyncSerial
    {
       //创建串行队列
       dispatch_queue_t queue = dispatch_queue_create("xc.download", DISPATCH_QUEUE_SERIAL);
      
       //异步函数
       dispatch_async(queue, ^{
           NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
       });
       dispatch_async(queue, ^{
           NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
       });
    }
    

    同步函数 + 并发队列

    //同步函数 + 并发队列 ;不会开线程的,因为是同步函数,
    //同步函数不具备开线程的能力,所以任务是串行执行的
    -(void)syncConcurrent
    {
        //创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("xc.download", DISPATCH_QUEUE_CONCURRENT);
        
        //同步函数
        dispatch_sync(queue, ^{
            NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
        });
    }
    

    同步函数 + 串行队列

    //同步函数 + 串行队列 ;
    //不会开线程的,因为是同步函数,同步函数不具备开线程的能力,所以任务是串行执行的
    -(void)syncSerial
    {
      //创建串行队列
      dispatch_queue_t queue = dispatch_queue_create("xc.download", DISPATCH_QUEUE_SERIAL);
      
      //异步函数
      dispatch_sync(queue, ^{
          NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
      });
      dispatch_sync(queue, ^{
          NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
      });
    }
    

    只有:异步函数 + 并发队列:次方法可以开多条线程,队列任务是异步(即并发)执行的,这样才是并发执行。其余几种组合都是串行执行
    gcd里面开多少条线程不是我们决定的,由系统决定

    全局并发队列

     //获得全局队列
        /*
            1,第一个参数:优先级
      系统原来就带有的 ,
           2,第二个参数暂时没什么用,传0 即可
         */
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    

    GCD主队列

      1. 主队列是GCD自带的一种特殊的串行队列
      2.只要在主队列任务,都会放到主线程中去执行
      3.使用     dispatch_get_main_queue()获得主队列
    

    队列特点是

    1. 封装任务, 2. 安排任务在哪个线程中去执行。

    主队列特点

    主队列,会把他放到主线程中去执行,如果主队列有任务在执行,
    那么主队列会暂停调用队列中的任务,知道主线程空闲为止

    异步函数 + 主队列

    //异步函数 + 主队列 。 不会开线程,所有任务主线程中去执行
    -(void)asyncMain
    {
      //获取主队列
      dispatch_queue_t queue = dispatch_get_main_queue() ;
      //异步函数
      dispatch_async(queue, ^{
          NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
      });
      dispatch_async(queue, ^{
          NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
      });
    }
    
    注意:同步函数 + 主队列 ,如果 在主线程中调用会报 错误 。 只能开启一条子线程,在 调用 同步函数 + 主队列 就可以了
    //同步函数 + 主队列 。 错误。会造成死锁,。 因为主队列只能在主线程中执行,,
    
    //如果 主线程 调用syncMain,方法会死锁,
    因为调用方法发现 dispatch_sync 是同步函数,同步函数的任务放到  
    主队列,然后因为是主队列,会把他放到主线程中去执行,而如果主线程调  
    用了syncMain方法,里面是同步函数,同步函数在等主线程任务执行  
    结束,而主线程在等同步函数执行完毕,这样就造成了死锁。所以要用  
    子线程去调用
    */
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
    //开启子线程调用
        [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
    }
    -(void)syncMain
    {
        //获取主队列
        dispatch_queue_t queue = dispatch_get_main_queue() ;
        //同步函数
        dispatch_sync(queue, ^{
            NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
        });
    }
    

    总结下:只有在并发队列和异步函数,才会开启新线程。
    1,并发队列+异步函数 = 有开启新线程,并发执行任务
    2,串行队列+异步函数 = 有开一天新线程,串行执行
    3,其余情况都是,不会开启新线程,串行执行 (例:并发队列,同步函数 or串行队列 + 异步函数 等)

    GCD 线程间的通信

       //GCD间的线程通信
        //创建子线程下载图片
        //获得全局主队列
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //子线程下载图片
            NSString *str =  @"http://static.firefoxchina.cn/img/201710/4_59e999c8e6aa50.jpg";
            NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:str]];
            UIImage *image = [UIImage imageWithData:data];
            NSLog(@"%@" ,[NSThread currentThread]);
           
            //回到主线程更新UI ,注意 同步函数 和 主队列。死锁情况哟
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageview.image = image ;
                NSLog(@"%@" ,[NSThread currentThread]);
            });
        });
        
    
    

    GCD一些常用方法

    1. 延迟调用方法
        /**
         方法一:延时调用改方法
         参数1: 调用的方法
         参数2:方法的参数
        参数3:延时的时间
         */
        [self performSelector:@selector(run:) withObject:nil afterDelay:2.0];
    
    
    /**
         方法二
         延时2s调用 run方法
         @param run: 方法
         */
        [NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(run:) userInfo:nil repeats:YES];
    
      /**
        方法三
            延时2s
         DISPATCH_TIME_FOREVER
         @param DISPATCH_TIME_NOW ->从现在开始
         @param int64_t 延迟的时间
         @return
    dispatch_get_main_queue 我们可以改变队列,来改变延时后在那个线程中执行
         */
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
        });
    
    

    栅栏函数

    并发队列多个任务,那么我们想要某个任务,最后执行怎么办 ,所以用栅栏函数
    注意:栅栏函数不能使用 全局并发队列,不然栅栏函数没有效果

    //栅栏函数
    -(void)barriermethod
    {
        //创建并发队列 。栅栏函数注意不能用全局并发队列
        dispatch_queue_t queue = dispatch_queue_create("xc_queue", DISPATCH_QUEUE_CONCURRENT);
        //异步函数 。 3个人任务并发执行,无顺序 。 那么怎么给他们安排顺序呢。 ----->栅栏函数。
        dispatch_async(queue, ^{
            for (int i = 0; i< 100; i++) {
                NSLog(@"downLoad1 = %@" ,[NSThread currentThread]);
            }
        });
        dispatch_async(queue, ^{
            for (int i = 0; i< 100; i++) {
                NSLog(@"downLoad2 = %@" ,[NSThread currentThread]);
            }
        });
        //我们需要任务3最后执行。所以我们用栅栏函数
        //栅栏
        dispatch_barrier_async(queue, ^{
            NSLog(@"x-x-xx");
        });
        dispatch_async(queue, ^{
            NSLog(@"downLoad3 = %@" ,[NSThread currentThread]);
        });
    }
    

    GCD的快速迭代 ,迭代就是遍历的意思

    for 循环就是迭代。 for循环是同步的

    //dispatch快速迭代
    -(void)apply
    {
        /**
         快速迭代, dispatch_apply 会开启子线程去遍历。
         @param iterations#> 迭代的次数 description#>
         @param queue#> 队列。只能传并发队列,传串行队列就和for一样了没有意义,不能传主队列,可以全局并发队列 description#>
         @param size_t index 索引
         */
        dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
            NSLog(@"%zd --%@",index,[NSThread currentThread]);
        });
    }
    /* 打印结果。有多个线程
    2017-10-23 23:33:19.605 NSTread基本使用[1293:46453] 2 --<NSThread: 0x6000002603c0>{number = 4, name = (null)}
    2017-10-23 23:33:19.605 NSTread基本使用[1293:46418] 0 --<NSThread: 0x608000079c40>{number = 1, name = main}
    2017-10-23 23:33:19.606 NSTread基本使用[1293:46455] 3 --<NSThread: 0x6080002676c0>{number = 5, name = (null)}
    2017-10-23 23:33:19.606 NSTread基本使用[1293:46453] 4 --<NSThread: 0x6000002603c0>{number = 4, name = (null)}
    2017-10-23 23:33:19.606 NSTread基本使用[1293:46418] 5 --<NSThread: 0x608000079c40>{number = 1, name = main}
    2017-10-23 23:33:19.606 NSTread基本使用[1293:46455] 6 --<NSThread: 0x6080002676c0>{number = 5, name = (null)}
    2017-10-23 23:33:19.606 NSTread基本使用[1293:46453] 7 --<NSThread: 0x6000002603c0>{number = 4, name = (null)}
    2017-10-23 23:33:19.606 NSTread基本使用[1293:46418] 8 --<NSThread: 0x608000079c40>{number = 1, name = main}
    2017-10-23 23:33:19.606 NSTread基本使用[1293:46455] 9 --<NSThread: 0x6080002676c0>{number = 5, name = (null)}
    2017-10-23 23:33:19.605 NSTread基本使用[1293:46452] 1 --<NSThread: 0x600000260380>{number = 3, name = (null)}
    */
    

    队列组。

    队列组里面的任务的进行情况。我们可以知道。是否完成,如果完成可以拿到这个完成事件

    //队列组 ,简写步骤 1
    -(void)groupMethod
    {
        //1.创建队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        //2.创建队列组
        dispatch_group_t group = dispatch_group_create() ;
        
        //3.异步函数创建,并队列组管理
        dispatch_group_async(group, queue, ^{
            for (int i = 0; i < 100; i++) {
                NSLog(@"downLoad1 = %@ , i = %d" ,[NSThread currentThread],i);
            }
        });
        dispatch_group_async(group, queue, ^{
            for (int i = 0; i < 100; i++) {
                NSLog(@"downLoad2 = %@ , i = %d" ,[NSThread currentThread],i);
            }
        });
        //离开通知,监听到
        dispatch_group_notify(group, queue, ^{
            NSLog(@"队列组任务完成。离开,在这里说明上面任务都完成了");
        });
        //和上面dispatch_group_notify 效果一样,特点队列组的任务完成后他才会执行
        //dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    }
    

    比较老一点的队列组写法

    //队列组 第二种
    -(void)groupMethod2
    {
        //1.创建队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        //2.创建队列组
        dispatch_group_t group = dispatch_group_create() ;
        
        //3.在该方法后面的异步任务会被纳入到队列的监听范围
        dispatch_group_enter(group);
        //4.异步函数创建,并队列组管理
        dispatch_async(queue, ^{
            for (int i = 0; i< 100; i++) {
                NSLog(@"downLoad1 = %@" ,[NSThread currentThread]);
            }
            //5. 告诉队列组任务执行完毕了,离开群组  dispatch_group_leave(group);
     
            dispatch_group_leave(group);
        });
        
        //dispatch_group_enter(group);   dispatch_group_leave(group);配对的,必须以前出现
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            for (int i = 0; i< 100; i++) {
                NSLog(@"downLoad2 = %@" ,[NSThread currentThread]);
            }
            dispatch_group_leave(group);
        });
        //离开通知,监听到
        dispatch_group_notify(group, queue, ^{
            NSLog(@"队列组任务完成。离开,这个 内部是异步的");
        });
    }
    
    学习记录:如有不妥。请大神指出,😁😄。

    NSOperation简单的入门
    NSThread概念以及入门

    相关文章

      网友评论

          本文标题:GCD的简单使用

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