多线程 —— GCD

什么是GCD
全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”
C语言,提供了非常多强大的函数

GCD的优势
GCD是苹果公司为多核并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核、四核)
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

GCD中有2个核心概念
任务执行什么操作
队列:用来存放任务

GCD的使用就2个步骤
1、定制任务:确定想做的事情
2、将任务添加到队列
GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循队列FIFO原则:先进先出,后进后出

执行任务
GCD中有2个用来执行任务的函数
同步的方式执行任务

dispatch_sync(dispatch_queue_t  queue, dispatch_block_t  block);

queue:队列
block:任务
异步的方式执行任务

dispatch_async(dispatch_queue_t  queue,dispatch_block_t  block);

同步和异步的区别
同步:在当前线程中执行
异步:在另一条线程中执行

队列的类型(GCD中有三种队列类型)
  1. The main queue: 与主线程功能相同。实际上,提交至main queue的任务会在主线程中执行。main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列。
  2. Global queues: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:高、中(默认)、低三个优先级队列。可以调用dispatch_get_global_queue函数传入优先级来访问队列。
  3. 用户队列: 用户队列 (GCD并不这样称呼这种队列, 但是没有一个特定的名字来形容这种队列,所以我们称其为用户队列) 是用函数 dispatch_queue_create 创建的队列. 这些队列是串行和并行的。正因为如此,它们可以用来完成同步机制, 有点像传统线程中的mutex。

常用的函数:

dispatch_resume() 继续执行线程

dispatch_suspend() 挂起线程 中断线程

dispatch_sync() 同步添加操作。他是等待添加进队列里面的操作完成之后再继续执行。

dispatch_async () 异步添加进任务队列,它不会做任何等待

dispatch_get_main_queue() 主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行

dispatch_queue_create() 创建一个队列

第一个参数是一个标签,这纯是为了debug。Apple建议我们使用倒置域名来命名队列,比如“com.dreamingwish.subsystem.task”。这些名字会在崩溃日志中被显示出来,也可以被调试器调用这在调试中会很有用,所有尽量不要重名了。
dispatch_get_global_queue() 全局并发队列,并由整个进程共享, 可设定优先级来选择高、中、低, 后台优先级队列。说明:全局并发队列的优先级
dispatch_group_wait 设置等待时间,在等待时间结束后,如果还没有执行完任务组,则返回。返回0代表执行成功,非0则执行失败

void dispatch_apply(size_t iterations, dispatch_queue_t queue,void (^block)(size_t)) 这个函数调用单一block多次,并平行运算,然后等待所有运算结束,作用是把指定次数指定的block添加到queue中

第一个参数是迭代次数
第二个是所在的队列
第三个是当前索引

容易混淆的术语
同步异步决定了要不要开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力

并发串行决定了任务的执行方式
并发多个任务并发(同时)执行
串行一个任务执行完毕后,再执行下一个任务

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
四种组合方式:
1、串行队列,同步执行任务
<span style="font-size:18px;"><span style="font-size:18px;">// 1. 创建一个串行队列
    //参数:1. 队列标签 2. 队列的属性
    dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"开始!!");

    // 2. 同步执行任务
    // 一般只要使用”同步“执行,串行队列对添加的同步任务,会立马执行
    dispatch_sync(queue, ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
</span></span>

2、串行队列,异步(开启多个线程)执行任务
<span style="font-size:18px;"><span style="font-size:18px;"> // 1. 串行队列
    // 下面两种写法是一样的
//    dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue = dispatch_queue_create("itcast", NULL);

    
    // 2. 异步执行
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
</span></span>

3、并行队列,同步执行任务
<span style="font-size:18px;"><span style="font-size:18px;">//1. 并行队列
    dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 同步执行任务
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
</span></span>

4、并行队列,异步执行任务
<span style="font-size:18px;"><span style="font-size:18px;"> //1. 并行队列
    dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 异步执行任务
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
</span></span>


主队列:
<span style="font-size:18px;">/**
 主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
 异步执行: 会开新线程,在新线程执行
 结果: 不开线程, 只能在主线程上面,顺序执行!
 */
- (void)gcdTest5
{
    // 1. 获得主队列-> 程序启动,--> 至少有一个主线程-> 一开始就会创建主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    NSLog(@"1----");
    
    // 2. 异步执行任务
    for (int i = 0; i < 10; i++) {
        NSLog(@"调度前---");
        // 异步:把任务放到主队列里,但是不需要马上执行
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
        NSLog(@"睡会");
        [NSThread sleepForTimeInterval:2.0];
    }
    NSLog(@"完成----");
}
</span>

/**

 主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.

 同步执行:要马上执行

 结果:死锁

 */


同步任务的作用
<span style="font-size:18px;">#pragma GCD- 同步任务的作用
- (void)gcdTest7
{
    // 并发队列
    dispatch_queue_t  queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
    
    /**
     例子:有一个小说网站
     - 必须登录,才能下载小说
     
     有三个任务:
     1. 用户登录
     2. 下载小说A
     3. 下载小说B
     */
    // 添加任务
    // 同步任务,需要马上执行。 不开新线程
    dispatch_sync(queue, ^{
        NSLog(@"用户登录 %@", [NSThread currentThread]);
    });
    //
    dispatch_async(queue, ^{
        NSLog(@"下载小说A %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"下载小说B %@", [NSThread currentThread]);
    });
}
</span>

全局队列:也是并发队列
/**
 全局队列跟并发队列的区别
 1. 全局队列没有名称 并发队列有名称
 2. 全局队列,是供所有的应用程序共享。
 3. 在MRC开发,并发队列,创建完了,需要释放。 全局队列不需要我们管理
 */
#pragma CGD - 全局队列
- (void)gcdTest8
{
    // 获得全局队列
    /**
     参数:第一个参数,一般 写 0(可以适配 iOS 7 & 8)
     iOS 7
     DISPATCH_QUEUE_PRIORITY_HIGH 2  高优先级
     DISPATCH_QUEUE_PRIORITY_DEFAULT 0  默认优先级
     DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
     DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
     
     iOS 8
     QOS_CLASS_DEFAULT  0
     
     第二个参数:保留参数 0
     */
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 添加异步任务
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
}

 队列的选择:

 串行队列异步执行

 -  开一条线程,顺序执行

 -  效率:不高,执行比较慢,资源占用小 -省电

 

 一般网络是3G,对想能要求不会很高。

 

 并发队列异步执行

 - 开启多条线程,并发执行

 - 效率:高,执行快,资源消耗大-》费电

 使用场合:

 - 网络WiFi,或者需要很快的响应,要求用户体验非常流畅。

 -对任务执行顺序没有要求

 

 -同步任务:一般只会在并发队列,需要阻塞后续任务。必须等待同步任务执行完毕,再去执行其他任务。"依赖"关系


线程间的通讯:(重点)
<span style="font-size:18px;">- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"%@", [NSThread currentThread]);
        // 耗时操作: 放在全局队列,异步执行
        // 1. url, 确定一个网络上的资源路径
        NSURL *url = [NSURL URLWithString:@"http://fe.topit.me/e/d1/12/1170068721aa112d1el.jpg"];
        
        // 2. 通过url可以下载对应的网络资源, 网络资源传输的都是二进制
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 3. 二进制转成图片
        UIImage *image = [UIImage imageWithData:data];
        
        // 4. 更新UI,在主线程-》 直接把任务添加到主队列,就会在主队列执行
        dispatch_async(dispatch_get_main_queue(), ^{
            self.iconView.image = image;
            NSLog(@"-----%@", [NSThread currentThread]);

        });
    });
    
}
</span>

其他用法:
<span style="font-size:18px;">#pragma mark - 调度组(分组)
- (void)group
{
    /**
     应用场景:
     开发的时候,有的时候出现多个网络请求都完成以后(每一个网络请求的事件长短不一定),再统一通知用户 
     
     
     比如: 下载小说:三国演义, 红楼梦, 金X梅
     */
    // 实例化一个调度组
    dispatch_group_t group = dispatch_group_create();
    
    // 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 任务添加到队列queue
    dispatch_group_async(group, queue, ^{
        NSLog(@"下载小说A---%@", [NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"下载小说B---%@", [NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"下载小说X---%@", [NSThread currentThread]);
    });
    
    // 获得所有调度组里面的异步任务完成的通知
//    dispatch_group_notify(group, queue, ^{
//        NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
//    });

    //注意点: 在调度组完成通知里,可以跨队列通信
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 更新UI,在主线程
        NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
    });
}
</span>

延时执行:
<span style="font-size:18px;"> dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"点我了-- %@", [NSThread currentThread]);

    });
</span>

一次性任务:
<span style="font-size:18px;"> dispatch_once(&onceToken, ^{
        NSLog(@"%----ld", onceToken);

        NSLog(@"真的执行一次么?");
    });</span>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值