美文网首页
ios中GCD的使用

ios中GCD的使用

作者: CoderLWG | 来源:发表于2018-02-05 11:12 被阅读268次

    1.GCD的核心概念

    GCD 核心概念:将任务添加到队列,指定任务执行的方法

    • 任务
      • 使用block 封装
      • block 就是一个提前准备好的代码块,在需要的时候执行
      • 任务的取出遵循队列的FIFO原则:先进先出
    • 队列(负责调度任务)
      • 串行队列: 一个接一个的调度任务
      • 并发队列: 可以同时调度多个任务
    • 任务执行函数(任务都需要在线程中执行!!)
      • 同步执行: 不会到线程池里面去获取子线程!
      • 异步执行: 只要有任务,CPU就会到线程池取子线程!(主队列除外!)
        小结:
      • 开不开线程,取决于执行任务的函数,同步不开,异步才能开
      • 开几条线程,取决于队列,串行开一条,并发可以开多条(异步)

    2. 串行队列,同步执行

    不会开启子线程,会顺序执行

    /**
         1.队列名称:
         2.队列的属性: DISPATCH_QUEUE_SERIAL 表示串行! NULL默认就表示串行!
         */
        dispatch_queue_t q = dispatch_queue_create("ios", NULL);
        
        //2.同步执行任务
        for (int i = 0; i < 10; i++) {
            dispatch_sync(q, ^{
                NSLog(@"%@ %d",[NSThread currentThread],i);
            });
        }
    
    打印结果如下:可以看出没有开启新线程,并且按顺序执行 image.png

    3.串行队列,异步执行

    会开新线程,会顺序执行

        //1.队列 - 串行
        dispatch_queue_t q = dispatch_queue_create("ios", NULL);
        
        //2.异步执行任务
        for (int i = 0; i < 10; i++) {
            dispatch_async(q, ^{
                NSLog(@"%@ %d",[NSThread currentThread],i);
            });
        }
        //在主线程!
        NSLog(@"come here");
    
    image.png

    4. 并发队列,异步执行

    会开启多条线程,异步执行

    //1.队列 - 并发 DISPATCH_QUEUE_CONCURRENT
        dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
        
        //2.异步执行任务
        for (int i = 0; i < 10; i++) {
            dispatch_async(q, ^{
                NSLog(@"%@ %d",[NSThread currentThread],i);
            });
        }
        //在主线程!
        NSLog(@"come here");
    
    image.png

    5. 并发队列,同步执行

    和 串行队列,同步执行 效果一样!
    // 会开线程吗? 顺序执行? come here?
    // 不会 顺序 最后

    //1.队列 - 并发 DISPATCH_QUEUE_CONCURRENT
        dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
        
        //2.同步执行任务
        for (int i = 0; i < 10; i++) {
            dispatch_sync(q, ^{
                NSLog(@"%@ %d",[NSThread currentThread],i);
            });
        }
        //在主线程!
        NSLog(@"come here");
    
    image.png

    6. 同步任务的作用

    在开发中,通常会将耗时操作放后台执行,有的时候,有些任务彼此有"依赖"关系!
    例子: 登录,支付,下载
    利用同步任务,能够做到任务依赖关系,前一个任务是同步任务,不执行完,队列就不会调度后面的任务

    dispatch_queue_t loginQueue = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
        //1.用户登录
        dispatch_sync(loginQueue, ^{
            NSLog(@"用户登录  %@",[NSThread currentThread]);
        });
        //2.支付
        dispatch_async(loginQueue, ^{
            NSLog(@"支付  %@",[NSThread currentThread]);
        });
        //3.下载
        dispatch_async(loginQueue, ^{
            NSLog(@"下载  %@",[NSThread currentThread]);
        });
        for (int i = 0; i< 10; i++) {
            NSLog(@"......%@",[NSThread currentThread]);
        }
    
    image.png

    7. 同步任务增强

    可以队列调度多个任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这就是依赖关系

    //队列
        dispatch_queue_t q = dispatch_queue_create("tanzhouios", DISPATCH_QUEUE_CONCURRENT);
        //任务
        void (^task)()=^{
            for (int i = 0; i < 10; i++) {
                NSLog(@"%d   %@",i ,[NSThread currentThread]);
                if (i==5) {
                    //1.用户登录
                    dispatch_sync(q, ^{
                        for (int i = 0; i < 5; i++) {
                            NSLog(@"用户登录  %@",[NSThread currentThread]);
                        }
                    });
                }
            }
            //2.支付
            dispatch_async(q, ^{
                NSLog(@"支付  %@",[NSThread currentThread]);
            });
            
            //3.下载
            dispatch_async(q, ^{
                NSLog(@"下载  %@",[NSThread currentThread]);
            });
       
        };
        dispatch_async(q, task);
    
    image.png

    8.全局队列

    1.本质上并发队列
    2.创建一个全局队列方法
    dispatch_get_global_queue(long identifier, unsigned long flags)
    参数1: 涉及到系统适配
    iOS 8 服务质量
    QOS_CLASS_USER_INTERACTIVE 用户交互(希望线程快速被执行,不要用好使的操作)
    QOS_CLASS_USER_INITIATED 用户需要的(同样不要使用耗时操作)
    QOS_CLASS_DEFAULT 默认的(给系统来重置队列的)
    QOS_CLASS_UTILITY 使用工具(用来做耗时操作)
    QOS_CLASS_BACKGROUND 后台
    QOS_CLASS_UNSPECIFIED 没有指定优先级
    iOS 7 调度的优先级
    - DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
    - DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
    - DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
    - DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
    提示:尤其不要选择BACKGROUND 优先级,服务质量,线程执行会慢到令人发指!!!
    参数2: 为未来使用的一个保留,现在始终给0.

     dispatch_queue_t q = dispatch_get_global_queue(0, 0);
        
        for (int i = 0; i< 10; i++) {
            dispatch_async(q, ^{
                NSLog(@"%@  %d",[NSThread currentThread],i);
            });
        }
        NSLog(@"come here");
    
    image.png

    9.全局队列 & 并发队列区别

    1> 名称,并发队列可以取名字,适合于企业开发跟踪错误
    2> release,在MRC 中使用并发队列 需要release
    dispatch_release(q)
    ARC 情况下不需要release !
    全局队列 & 串行队列

    全局队列: 并发,能够调度多个线程,执行效率高
    - 费电
    串行队列:一个一个执行,执行效率低
    - 省电
    判断依据:用户上网方式
    - WIFI : 可以多开线程 5~6条
    - 流量 : 尽量少开线程 2~3条

    10.GCD延时执行

    从现在开始,进过多少纳秒之后,让 queue队列,调度 block 任务,异步执行!
    参数:
    1.when
    2.queue
    3.block

    NSLog(@"come here"); 
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.00003 * NSEC_PER_SEC));
        dispatch_after(when, dispatch_queue_create("ios", NULL), ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
    
    image.png

    11. GCD一次执行

    苹果提供的 一次执行机制,不仅能够保证一次执行!而且是线程安全的!!
    苹果推荐使用 gcd 一次执行,效率高
    不要使用互斥锁,效率低!
    从下图可以看出,只执行一次
    在执行前onceToken值为0,执行后值为-1

    for (int i = 0 ; i < 10; i++) {
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                [self once];
            });
        }
    
    -(void)once{
        static dispatch_once_t onceToken;
        NSLog(@"%ld",onceToken);
        dispatch_once(&onceToken, ^{
            //只会执行一次!!
            NSLog(@"执行了%@",[NSThread currentThread]);
        });
    }
    
    image.png
    image.png

    11.GCD调度组

    //1.队列
        dispatch_queue_t q = dispatch_get_global_queue(0, 0);
        
        //2.调度组
        dispatch_group_t group = dispatch_group_create();
        
        //3.添加任务,让队列调度,任务执行情况,最后通知群组
        dispatch_group_async(group, q, ^{
            NSLog(@"download A%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, q, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"download B%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, q, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"download C%@",[NSThread currentThread]);
        });
        
        //4.所有任务执行完毕后,通知
        //用一个调度组,可以监听全局队列的任务,主队列去执行最后的任务
        //dispatch_group_notify 本身也是异步的!
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            //更新UI,通知用户
            NSLog(@"下载完成,合并图片 %@",[NSThread currentThread]);
        });
    
    image.png

    通常情况下,在group中加入任务block是这样的

    dispatch_group_async(group, queue, ^{
        block();
    });
    这个写法等价于
    dispatch_async(queue, ^{
        dispatch_group_enter(group);
        block()
        dispatch_group_leave(group);
    });
    

    如果要把一个异步任务加入group,这样就行不通了:

    dispatch_group_async(group, queue, ^{
        [self performBlock:^(){
            block();
        }];
        //未执行到block() group任务就已经完成了
    });
    这时需要这样写:
    dispatch_group_enter(group);
    [self performBlock:^(){
        block();
        dispatch_group_leave(group);
    }];
    异步任务回调后才算这个group任务完成
    

    12.主队列

    主队列 & 串行队列的区别
    都是 一个一个安排任务
    队列特点:FIFO
    并发队列: 可以调度很多任务
    串行队列,:必须等待一个任务执行完成,再调度另外一个,最多只能开启一条线程
    主队列:以FIFO调度任务,如果主线程上有任务在执行,主队列就不会调度任务,主要是负责在主线程上执行任务

    //主队列是专门负责在主线程上调度任务的队列 --> 不会开线程
        //1.队列 --> 已启动主线程,就可以获取主队列
        dispatch_queue_t q = dispatch_get_main_queue();
        
        //2.异步任务
        dispatch_async(q, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
        NSLog(@"come here");
    
    // 这种当时执行主队列,同步会造成死锁   
     NSLog(@"这里!!");
        //1.队列 --> 已启动主线程,就可以获取主队列
        dispatch_queue_t q = dispatch_get_main_queue();
        
        //2.同步任务  ==> 死锁
        dispatch_sync(q, ^{
            NSLog(@"能来吗? ");
        });
        NSLog(@"come here");
    
    主队列同步任务(不死锁的),这种情况下不会造成死锁
     void (^task)() = ^{
            NSLog(@"这里!!%@",[NSThread currentThread]);
            //1.队列 --> 已启动主线程,就可以获取主队列
            dispatch_queue_t q = dispatch_get_main_queue();
            //2.同步任务
            dispatch_sync(q, ^{
                NSLog(@"能来吗? %@",[NSThread currentThread]);
            });
            NSLog(@"come here");
        };
        
        //会开线程吗??
        dispatch_async(dispatch_get_global_queue(0, 0), task);
    
    image.png

    13.各种队列的执行效果

    image.png

    注意
    使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列

    14.dispatch的其他用法

    使用dispatch_apply函数能进行快速迭代遍历
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
        // 执行10次代码,index顺序不确定
    });
    
    dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
    在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
    这个queue不能是全局的并发队列
    

    相关文章

      网友评论

          本文标题:ios中GCD的使用

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