GCD的简单使用

2017-10-22  本文已影响15人  3fbfd3c15df9

GCD简单使用

  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概念以及入门

上一篇下一篇

猜你喜欢

热点阅读