ios中GCD的使用

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调度组

dispatch_group_async的使用

//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_barrier_async

dispatch_barrier_async的作用?
在并行队列中,为了保持某些任务的顺序,需要等待一些任务完成之后才能继续执行,使用barrier来等待之前任务完成,避免数据竞争等问题。
dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后在执行barrier函数的处理,执行结束之后再回复之前的动作继续执行。
注意:
使用dispatch_barrier_async,该函数只能搭配自定义并行队列
dispatch_queue_t使用,不能使用:
dispatch_get_global_queue,否则dispatch_barrier_async的作用会和dispatch_async一模一样。

15.信号量dispatch_semaphore

信号量使用
信号量:Semaphore是通过计数的方式来标识线程是否是等待或继续执行的。

dispatch_semaphore_create(int) // 创建一个信号,并初始化信号的计数大小
/* 等待信号,并且判断信号量,如果信号量计数大于等于你创建时候的信号量的计数,就可以通过,继续执行,并且将你传入的信号计数减1,
 * 如果传入的信号计数小于你创建的计数,就表示等待,等待信号计数的变化
 *  如果等待的时间超过你传入的时间,也会继续下面操作
 *   第一个参数:semaphore 表示信号量
 *   第二个参数:表示等待的时间
 *    返回int 如果传入的信号计数大于等于你创建信号的计数时候,返回0.  反之,返回的不等于0
 */
 int result = dispatch_semaphore_wait(dispatch_semaphore_t  semaphore,time outTime);// 表示等待,也是阻碍线程
// 表示将信号计数+1
dispatch_semaphore_signl(dispatch_semaphore_t semaphore);

16.dispatch的其他用法

使用dispatch_apply函数能进行快速迭代遍历
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
    // 执行10次代码,index顺序不确定
});

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容