GCD与队列

来源:互联网 发布:越知月光图片 编辑:程序博客网 时间:2024/05/17 23:41
// MARK: - 全局队列
/**
 开多条线程,不顺序执行!
 
 全局队列本身就是一个并发队列
 
 问题:
 
 一. 全局队列 & 并发队列的区别
 
 1. 全局队列只需要 get
    * 没有 name
 2. 并发队列需要 create
    * 如果在 MRC 开发,需要自己 release
    - dispatch_release(q);
    * 有 name
 
 - 关于名称,和 NSThread 的 name 属性类似
 
 关于队列的选择
 - 建议:前期日常开发中,建议选择 全局队列!
 - 自己创建的并发队列,通常在开发第三方框架的时候会使用!
 
 二. 全局队列 & 串行队列的选择
 
 - 全局队列
    * 调度任务的时候,多个线程,而且是顺序不固定
    * 并发能力好
    * 效率高,速度快,费电,费钱
 - 串行队列(斯坦福大学)
    * 调度任务,只有一条线程,顺序执行每一个任务
    * 并发能力差
    * 效率差,速度慢,省电,省钱
    * 用户并不是什么时候都会希望快的!
 
 选择依据:
 
 - 如果是 WIFI 情况,使用全局队列,开启线程的数量 6 条左右
 - 如果是 3G/4G,使用串行队列,如果使用全局队列,开启线程的数量控制在 2~3 条
 */
- (void)gcdDemo10 {
    
    // 全局队列,是 gcd 为了方便程序员的多线程开发提供的 dispatch_get_global_queue
    // 本身就是一个并发队列
    /**
     参数:
     
     关于服务质量:苹果官方有一个视频 XPC 框架(用在 MAC 上开发的一个框架)和 GCD 的一个提升

     1. iOS 8.0 告诉队列执行任务的"服务质量quality of service"
         QOS_CLASS_USER_INTERACTIVE 0x21,               用户交互(希望尽快完成,用户对结果很期望,不要放太耗时操作)
         QOS_CLASS_USER_INITIATED 0x19,                 用户期望(不要放太耗时操作)
         QOS_CLASS_DEFAULT 0x15,                        默认(不是给程序员使用的,用来重置对列使用的)
         QOS_CLASS_UTILITY 0x11,                        实用工具(耗时操作,可以使用这个选项)
         QOS_CLASS_BACKGROUND 0x09,                     后台
         QOS_CLASS_UNSPECIFIED 0x00,                    未指定
        iOS 7.0 之前 优先级
         DISPATCH_QUEUE_PRIORITY_HIGH 2                 高优先级
         DISPATCH_QUEUE_PRIORITY_DEFAULT 0              默认优先级
         DISPATCH_QUEUE_PRIORITY_LOW (-2)               低优先级
         DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN   后台优先级
        
        提示:不要选择 BACKGROUND 的选项,苹果认为:BACKGROUND 表示用户不需要知道任务什么时候完成!
        选择这个选项,速度慢的令人发指!不利于调试!
     
        关于优先级,不要搞太负责,就用最简单的
        
        结论:如果要做 iOS 8.0 & iOS 7.0 的适配:
        dispatch_get_global_queue(0, 0);
     
        提示:如果在今后,iOS 8.0 & 9.0 适配的时候,应该选择 QOS_CLASS_UTILITY
     
     2. Flags that are reserved for future use
        标记是为了未来使用保留的!这个参数应该永远指定为 0
     */
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    // 2. 异步执行
    for (int i = 0; i < 10; ++i) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"come here");
}

// MARK: - "主队列同步任务不死锁"(暂时不要求掌握)
- (void)gcdDemo9 {
    // 1. 并发队列
    dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 任务
    void (^task)() = ^ {
        NSLog(@"%@", [NSThread currentThread]);
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"come here %@", [NSThread currentThread]);
        });
        
        NSLog(@"hahah %@", [NSThread currentThread]);
    };
    
    // 3. 异步执行任务
    dispatch_async(q, task);
}

// MARK: - "同步"执行任务的作用
// 同步任务的增强演练(暂时不要求掌握)
/**
 提示:通过 block 的嵌套,能够让任务非常的复杂!
 复杂到,很难用一张表格来描述各种组合!
 
 *** 需要理解队列&执行任务的函数的特点!
 
 强烈建议:多线程的目的,是将耗时操作放在后台,不要把代码写的太复杂!
 */
- (void)gcdDemo8 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 任务
    void (^task)() = ^ {
        dispatch_sync(q, ^{
            NSLog(@"Login %@", [NSThread currentThread]);
        });
        dispatch_async(q, ^{
            NSLog(@"Download A %@", [NSThread currentThread]);
        });
        dispatch_async(q, ^{
            NSLog(@"Download B %@", [NSThread currentThread]);
        });
    };
    
    // 3. 异步执行 task
    dispatch_async(q, task);
}

// 同步任务的基础演练!
- (void)gcdDemo7 {
    // 例子:用户登录,下载A,下载B
    // 要求:必须用户登录成功后,才允许下载
    
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 同步执行任务 - 作用可以让一些异步执行的任务 "依赖" 某一个特殊的任务(用户登录)
    dispatch_sync(q, ^{
        NSLog(@"Login %@", [NSThread currentThread]);
    });
    dispatch_async(q, ^{
        NSLog(@"Download A %@", [NSThread currentThread]);
    });
    dispatch_async(q, ^{
        NSLog(@"Download B %@", [NSThread currentThread]);
    });
}

// MARK: - 主队列
/**
 主队列 同步执行
 
 提问:开线程吗?come here?
 猜测:不开,最后!
 结果:会造成死锁!
 
 关于死锁:记住主队列的特点就容易理解!主线程有任务就暂时不调度任务!
 */
- (void)gcdDemo6 {

    // 1. 队列
    dispatch_queue_t q = dispatch_get_main_queue();
    NSLog(@"!!!!");
    
    // 2. 同步执行
    dispatch_sync(q, ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
    
    NSLog(@"come here");
}

/**
 主队列 "异步"执行 - 线程间通讯的!
 
 主队列,专门负责在主线程上调度任务,所有的任务执行就应该在主线程上执行!
 
 提问:开线程吗?come here?
 猜测:不开? 最后/不确定!
 答案:不开,最前面!
 */
- (void)gcdDemo5 {
    // 1. 队列 - 程序一启动,主线程就已经存在,主队列也同时 就存在了,只需要获取不需要创建
    dispatch_queue_t q = dispatch_get_main_queue();
    
    // 2. 异步执行
    for (int i = 0; i < 10; ++i) {
        dispatch_async(q, ^{
            NSLog(@"%@ - %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"睡会");
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"come here");
}

// MARK: - 并发队列
/**
 并发队列,同步执行任务
 和 串行队列"同步执行"任务结果是一样的
 
 提问:开线程吗?开几条线程?come here?
 猜测:不开!没有条!最后!
 结果:全中!
 */
- (void)gcdDemo4 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 同步执行
    for (int i = 0; i < 10; ++i) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"come here");
}

/**
 并发队列,可以同时调度多个任务
 异步执行:可以开启线程
 
 队列,本质上仍然是按照"先进先出"的方式来调度任务,而任务具体执行情况,是由 CPU 决定!
 
 提问:开线程吗?开几条线程?come here?
 猜测:开!N条!不确定!
 答案:开线程,线程条数由GCD决定,不确定!
 */
- (void)gcdDemo3 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 异步执行
    for (int i = 0; i < 10; ++i) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"come here");
}

// MARK: - 串行队列
// 串行队列 同步执行
/**
 提问:开线程吗?开几条线程?come here?
 猜测:不开!没有!最后!
 答案:全中
 
 原因:同步任务不会开启线程,当前代码不执行完,不会执行后续的代码
 */
- (void)gcdDemo2 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("itheima", NULL);
    
    // 2. 执行任务的函数
    for (int i = 0; i < 10; ++i) {
        // 10 个异步
        dispatch_sync(q, ^{
            NSLog(@"%@", [NSThread currentThread]);
        });
        
        NSLog(@"---- %d", i);
    }
    
    NSLog(@"come here");
}

// 串行队列 异步执行
/**
 提问:开线程吗?开几条线程?come here?
 猜测:开!不确定!不确定!
 答案:开!开一条!不确定!
 
 原因:串行队列,一个接一个的调度任务!
 */
- (void)gcdDemo1 {
    // 1. 队列
    /**
     参数:
     1. 队列的名称,类似于 NSThread 的 name 属性,是一个 C 语言的 字符串
     2. 队列的属性
        - DISPATCH_QUEUE_SERIAL(NULL)   串行队列
        - DISPATCH_QUEUE_CONCURRENT     并发队列
            使用 \ 拼接宏,使用宏即可
     */
//    dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_SERIAL);
    // 这种写法更常见
    dispatch_queue_t q = dispatch_queue_create("itheima", NULL);
    
    // 2. 执行任务
    for (int i = 0; i < 10; ++i) {
        // 10 个异步
        dispatch_async(q, ^{
            NSLog(@"%@", [NSThread currentThread]);
        });
    }
    
    NSLog(@"come here");
}


调度组
- (void)group2 {
    
    // 1. 群组-统一监控一组任务
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    // 1> 入组 -> 之后的 block 会被 group 监听
    // dispatch_group_enter 一定和 dispatch_group_leave 要配对出现
    dispatch_group_enter(group);
    dispatch_async(q, ^{
        NSLog(@"task1 %@", [NSThread currentThread]);
        
        // block 的末尾,所有任务执行完毕后,添加一个出组
        dispatch_group_leave(group);
    });
    // 2> 再次入组
    dispatch_group_enter(group);
    dispatch_async(q, ^{
        [NSThread sleepForTimeInterval:1.0];
        
        NSLog(@"task2 %@", [NSThread currentThread]);
        
        // block 的末尾,所有任务执行完毕后,添加一个出组
        dispatch_group_leave(group);
    });
    
    // 3. 群组结束
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        NSLog(@"OVER");
//    });
    
    // 4. 群组等待 -> 等到永远,死等,等到所有任务完成
    // 阻塞式的等待,群组任务不执行完,后续代码无法执行!
    // (知道就行)
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"come here");
}

// MARK: 调度组
// 应用场景:需要在所有异步任务执行完毕后,统一获得一个通知!
// 新浪微博提前将所有的缩略图(2k~3k)下载到本地,统一刷新表格,就可以针对不同的图片宽高设计UI
// 如果图片在服务器上,是无法直接获得宽高
- (void)group1 {
    // 1. 群组-统一监控一组任务
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    // 2. 添加任务
    // group 负责监控任务,queue 负责调度任务
    dispatch_group_async(group, q, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"任务1 %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, q, ^{
        NSLog(@"任务2 %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, q, ^{
        NSLog(@"任务3 %@", [NSThread currentThread]);
    });
    
    // 3. 监听所有任务完成 - 等到 group 中的所有任务执行完毕后,"由队列调度 block 中的任务异步执行!"
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 修改为主队列,后台批量下载,结束后,主线程统一更新UI
        NSLog(@"OK %@", [NSThread currentThread]);
    });
    
    NSLog(@"come here");
}


0 0