美文网首页
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