zoukankan      html  css  js  c++  java
  • 1. 关于GCD的使用(串并行队列和同步异步函数的问题)

    /*------------------------------ GCD使用 1.队列和任务------------------------------------------*/
    重点:1."串行队列"? "并发队列"? 2.block?
    {
        1.GCD(Grand Central Dispatch) ---- '牛逼的中枢调度器'!
        // C语言框架 / 自动管理线程的生命周期(创建/释放)
        
        推出GCD的目的:取代NSThread!"多核""并行"运算提出的解决方案!
        
        优点:
        <1> GCD 能够自动利用更多的CPU的核数(双核/四核)!
        <2> GCD 会自动管理线程的生命周期.
        
        程序员只需要告诉 GCD 想要执行的任务(代码)!
        
        2.GCD中的两个核心概念:
        
        "任务":
            想要做的事情/执行什么操作.
            GCD 中的任务定义在block中.
            void (^myBlock)() = ^{
                // 想要做的事情/任务
            }
        
        "队列":
            用来'存放'任务!
        
        队列 != 线程!
        队列中存放的任务最后都要由线程来执行!
        
        队列的原则:先进先出,后进后出(FIFO/ First In First Out)!
    
        队列的类型:
        <1> '串行'队列:(Serial Dispatch Queue)
        存放按顺序执行的任务!(一个任务执行完毕,再执行下一个任务)!
        
        // 创建一个串行队列
        dispatch_queue_t serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
        
        <2> '并发'队列:(Concurrent Dispatch Queue)
        存放想要同时(并发)执行的任务!
        
        // 创建一个并发队列
        dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);
        
        注意两个非常常用的特殊队列:
        
        <1> 主队列: // UI 操作放在主队列中执行!
            跟主线程相关联的队列!
            主队列是 GCD 自带的一种特殊的串行队列!
            主队列中的任务都会在主线程中执行!
        //获取主队列
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
        <2> 全局并发队列: // 一般情况下,并发任务都可以放在全局并发队列中!
        //获取全局并发队列
        dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    }
    /*-----------------------------------  GCD使用 2.执行任务 -------------------------------------*/
    重点:1."同步"函数!"异步"函数! 2.容易混淆的四个概念: '串行' ,'并发' ,"同步" ,"异步"之间的区别?
    {
        问题:串行队列中的任务必定按顺序执行吗?并发队列中的任务必定同时执行吗?
        
        GCD中有两个用来执行任务的函数:
        
        '同步'执行任务:
        dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)
        
        '异步'执行任务:
        dispatch_async(dispatch_queue_t queue, <#^(void)block#>)
    
        // <#dispatch_queue_t queue#> :队列
        // <#^(void)block#>:任务
        
        "同步""异步"的区别:
        "同步": 只能在'当前'线程中执行任务,不具备开启新线程的能力.
        "异步": 可以在''的线程中执行任务,具备开启新线程的能力.
        
        GCD 使用有两个步骤:
        <1> 将任务添加到队列中;
        <2> 选择同步还是异步的方式执行任务.
        
        注意:四个容易混淆的术语:
        '串行' ,'并发' ,"同步" ,"异步".
    }
    /*-------------------------------  GCD使用 3.各种队列的执行效果 ---------------------------------*/
    重点:1.掌握两个常用的组合!
    {
        常见的组合(掌握)
        1> dispatch_async + 全局并发队列 (可以开启多条线程)
        2> dispatch_async + 自己创建的串行队列 (开启一条线程)
        
        只有'异步'执行"并发"队列,才可以开启多条线程.
        
        注意:
        在主线程中同步执行主队列中的任务,会造成'主线程''主队列'相互等待,卡住主线程!
    }
    /*-------------------------------  GCD使用 4.线程间通信 ---------------------------------------*/
    重点:1.从子线程回到主线程(经典用法)! 2.两个注意点.
    {
        1.经典用法(子线程下载(耗时操作),主线程刷新UI):
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
                           // 执行耗时的异步操作...
            
                           dispatch_async(dispatch_get_main_queue(), ^{
                               
                               // 回到主线程,执行UI刷新操作
                               
                           });
                       });
    
        2.注意:
        <1> 需要设置按钮的image,建议先把按钮类型改为custom,才能保证设置成功
        <2> 属性名不能以new开头
    }
    /*-------------------------------  GCD使用 5.延时执行  -----------------------------------------*/
    重点:1.iOS常见的两种延时执行方式
    {
        iOS中的延时执行方式:
        // 定制好延时任务后,不会阻塞当前线程.
        
        <1> 调用 NSObject 方法:
        [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
        // 2秒后再调用self的run方法
        
        <2> GCD 函数实现延时执行:
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            // 2秒后执行这里的代码... 在哪个线程执行,跟队列类型有关
        });
        
        注意:
        不要使用sleep,会阻塞当前线程.
    }
    /*-------------------------------  GCD使用 6.队列组   ------------------------------------------*/
    重点:1.了解队列组的使用方法.
    {
        项目需求:
        首先:分别异步执行两个耗时操作;
        其次:等两次耗时操作都执行完毕后,再回到主线程执行操作.
        
        使用队列组(dispatch_group_t)快速,高效的实现上述需求.
        
        dispatch_group_t group = dispatch_group_create(); // 队列组
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 全局并发队列
        
        dispatch_group_async(group, queue, ^{         // 异步执行操作1
            // longTime1
        });
        
        dispatch_group_async(group, queue, ^{         // 异步执行操作2
            // longTime2
        });
        
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{     // 在主线程刷新数据
            // reload Data
        });
    }
    /*-------------------------------  GCD使用 7.一次性代码 ---------------------------------------*/
    重点:1.掌握一次性代码的实现.
    {
        一次性代码:
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            // 只执行一次的代码(这里面默认是线程安全的).
        });
    }
    /*--------------------------------------  补充: 单例设计模式 -----------------------------------*/
    重点:1.掌握单例!
    {
        1.单例简介:
        
        作用:
            保证程序在运行过程中,一个类只有一个实例对象.这个实例对象容易被外界访问!
            控制实例对象个数(只有一个),节约系统资源.
        
        使用场合:
            在整个应用程序中,共享一份资源(这份资源只需要创建初始化一次).
        
        举例:
            打印机/视图窗口/一些网络工具类等等
        
        // 懒汉式: 用到的时候再加载.
        // 饿汉式: 只要程序运行就加载. // 不需要掌握,也不要这么写!
        // 掌握懒汉式.
        
        2.单例实现:(两种方式:互斥锁(@synchronized(self))和一次性代码(dispatch_once));
        
        2.1互斥锁 @synchronized(self):
        
        <1>.在 .m 文件中保留一个全局的 static 的实例.
        
            static id _instance;
        
        <2>.重写若干方法(allocWithZone:和 copyWithZone:)并提供一个类方法让外界访问唯一的实例.
        
        //(1)重写 allocWithZone:方法,在这里创建唯一的实例(注意线程安全). //alloc 内部都会调用这个方法.
            +(instancetype)allocWithZone:(struct _NSZone *)zone {
                if (_instance == nil) { // 防止频繁加锁
                    @synchronized(self) {
                        if (_instance == nil) { // 防止创建多次
                            _instance = [super allocWithZone:zone];
                        }
                    }
                }
                return _instance;
            }
        
        //(2)重写 copyWithZone:方法.
            +(id)copyWithZone:(struct _NSZone *)zone
            {
                return _instance;
            }
        //(3)提供1个类方法让外界访问唯一的实例
            +(instancetype)shareSingleton
            {
                if (!_instance) { // 防止频繁加锁
                    @synchronized(self){
                        if (!_instance) { // 防止创建多次
                            _instance = [[self alloc] init];
                        }
                    }
                }
                return _instance;
            }
        
        2.2 一次性代码(dispatch_once):
        <1>.在 .m 文件中保留一个全局的 static 的实例.
        
        static id _instance;
        
        <2>.重写若干方法(allocWithZone:和 copyWithZone:)并提供一个类方法让外界访问唯一的实例.
        
        //(1)重写 allocWithZone:方法,在这里创建唯一的实例(注意线程安全).
        + (id)allocWithZone:(struct _NSZone *)zone
        {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                _instace = [super allocWithZone:zone];
            });
            return _instace;
        }
        
        //(2)重写 copyWithZone:方法.
        +(id)copyWithZone:(struct _NSZone *)zone
        {
            return _instance;
        }
        //(3)提供1个类方法让外界访问唯一的实例
        + (instancetype)shareSingleton
        {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                _instace = [[self alloc] init];
            });
            return _instace;
        }
    
        注意:在 ARC 和 MRC 中单例的实现方式略有不同. MRC 下单例的实现比 ARC 多了几个内存管理的方法:
        
        MRC 中增加如下方法的实现:
        - (instancetype)retain { return self; }
        - (NSUInteger)retainCount { return 1; }
        - (oneway void)release {}
        - (instancetype)autorelease { return self; }
        
        3.判断当前环境(ARC/MRC)
        
    #if __has_feature(objc_arc)
        // ARC
    #else
        // MRC
    #endif
        
        4.注意两个方法:
        // 面试问题:两个方法的区别?
        <1> +(void)load;
        // 当类加载到OC运行时环境(内存)中的时候,就会调用一次(一个类只会加载一次).
        // 程序一启动就会调用.
        // 程序运行过程中,只会调用1次.
        <2> +(void)initialize;
        // 当第一次使用这个类的时候(比如调用了类的某个方法)才会调用.
        // 并非程序一启动就会调用.
    }
  • 相关阅读:
    为什么32位系统最大支持4G内存??我自己悟出来了 终于 。。。。。
    关于 Delphi 中流的使用(1) 用 TMemoryStream(内存流) 入门 &&& 关于指针的迷惑,我自己问的.
    关于多态的理解,有助于理解TStream抽象类的多态机制。
    FP-Tree频繁模式树算法
    FP-Tree频繁模式树算法
    分布式系统阅读笔记(二十一)-----分布式系统设计(Google Case Study)
    分布式系统阅读笔记(二十一)-----分布式系统设计(Google Case Study)
    分布式系统阅读笔记(二十一)-----分布式系统设计(Google Case Study)
    链接挖掘算法之PageRank算法和HITS算法
    链接挖掘算法之PageRank算法和HITS算法
  • 原文地址:https://www.cnblogs.com/chnyang/p/4780205.html
Copyright © 2011-2022 走看看