zoukankan      html  css  js  c++  java
  • iOS中多线程知识总结(二)

    1.GCD

         GCD全称是Grand Central Dispatch,译为"强大的中枢管理器"

      1)什么是任务?什么是队列?

        任务和队列是GCD的核心.

        任务: 执行什么操作

        队列: 用来存放任务

          2)用GCD创建线程的两种方式. 

        01.使用并发队列创建

    //01 获得并发队列
        /*
         第一个参数:C语言的字符串 对队列的名称(com.520it.www.DownloadQueue)
         第二个参数:队列的类型
         DISPATCH_QUEUE_SERIAL  串行队列
         DISPATCH_QUEUE_CONCURRENT 并发队列
         */
        dispatch_queue_t queue = dispatch_queue_create("www.baidu.com", DISPATCH_QUEUE_CONCURRENT);
        
        //02 封装任务并把任务添加到队列
        dispatch_async(queue, ^{
            NSLog(@"download1---%@",[NSThread currentThread]);
        });

             02.使用全局队列创建

    //02 使用获得全局并发队列,开启子线程
    
            /*
         第一个参数:队列的优先级
         第二个参数:
         */
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        //02 封装任务并把任务添加到队列
        dispatch_async(queue, ^{
            NSLog(@"download1---%@",[NSThread currentThread]);
        });

          3)同步,异步,并发,串行

        同步函数: 在当前线程中执行任务,不具备开线程的能力

        异步函数: 在新的线程中执行任务,具备开线程的能力

        并发队列: 多个任务并发执行

               串行队列: 一个任务执行完之后,再执行下一个

      4)是否会开启线程

            01 异步函数+并发队列:开启多条线程,并发执行任务
            02 异步函数+串行队列:开启一条线程,串行执行任务
            03 同步函数+并发队列:不开线程,串行执行任务
            04 同步函数+串行队列:不开线程,串行执行任务
            05 异步函数+主队列:不开线程,在主线程中串行执行任务
            06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)

      5) 为什么使用同步函数+主队列(串行队列)会发生死锁?

        首先要明白主队列是在主线程中,而同步函数不具备开启子线程的能力.假设A正在主线程中执行,而在A执行的过程中又需要去执行B.由于同步函数+主队列并不会开启子线程,只能去主线程中执行,而主线程中A正在执行.由A需要C去执行,C又需要等A执行完毕,才能执行,就会造成死锁.

          6) GCD中常用的函数

        1)一次性代码,整个程序运行过程中只执行一次,可以用作创建单例,线程安全

    static dispatch_once_t onceToken;
        
        //内部的实现原理:最开始的时候onceToken==0 如果onceToken==0 那么就执行一次,onceToken=-1
        NSLog(@"%zd",onceToken);
        dispatch_once(&onceToken, ^{
            NSLog(@"once");
        });

        2)延迟函数

    dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_SERIAL);
        /*
         延迟2秒,然后再把任务提交到队列
         */
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
            
            NSLog(@"GCD----%@",[NSThread currentThread]);
        });

        3)遍历函数

    //快速迭代(并发队列):会开启子线程和主线程一起执行任务,所有的任务并发执行
        /*
         第一个参数:要遍历的次数
         第二个参数:队列 ~ 线程
         
         全局并发队列 == 自己创建的并发队列
         自己创建的串行队列 == for循环
         主队列:死锁
         */
        dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_CONCURRENT);
        dispatch_apply(10, queue, ^(size_t i) {
            NSLog(@"%zd---%@",i,[NSThread currentThread]);
        });

        4)栅栏函数

    //需求:1)有四个任务,开子线程并发的执行这四个任务
        //2)添加任务+++++++,但是要求必须要等1|2都执行完才执行++++,必须要等+++打印执行完才能执行后面的任务
        //3)所有的任务都在子线程中执行(dispatch_barrier_async)
        //栅栏 = 篱笆
        //01 获得并发队列
        dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_CONCURRENT);
        //注意:!!!! 栅栏函数在使用中不能使用全局并发队列(会丧失拦截的功能)
        //dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        //02 异步函数
        dispatch_async(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        
      //特点:拦截上面的任务必须等前面的任务执行完才执行当前的block,必须等当前的block快执行完才执行后面
        //dispatch_barrier_async 子线程中执行
        //dispatch_barrier_sync  当前线程
        dispatch_barrier_async(queue, ^{
            NSLog(@"+++++%@",[NSThread currentThread]);
       });
        
        dispatch_async(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"4----%@",[NSThread currentThread]);
        });

     4:NSOperation(操作队列)

         1)基本概念

        NSOperation本身是抽象类,只能使用它的子.分别是NSBlockOperation、NSInvocationOperation以及自定义继承自NSOperation的类

          2)操作队列的核心是: 队列 + 操作

        其实它的使用也很简单,就是先创建队列,然后把你封装的操作加到队列,操作队列默认的是并发队列

      3)NSInvocationOperation的使用(使用不多)   

    //01 创建队列
        /*
         操作队列:
         ① 自己创建(自定义)[并发队列*串行队列,默认是并发队列] [[NSOperationQueue alloc]init]
         ② 主队列[串行队列]                              [NSOperationQueue MainQueue]
         */
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //02 封装操作
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil];
        
        NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil];
        
        //03 把操作添加到队列
        [queue addOperation:op1];
        [queue addOperation:op2];

        4)NSBlockOperation

          1)怎么创建

    //01 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //02 封装操作
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2----%@",[NSThread currentThread]);
        }];
        
        //03 把操作添加到队列
        [queue addOperation:op1];  //addOperation 内部调用start方法
        [queue addOperation:op2];

          2) 设置依赖和监听(通常在下载的时候用的到)

    //01 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        NSOperationQueue *queue2 = [[NSOperationQueue alloc]init];
        
        //02 封装操作
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"3----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"4-下载电影-%@",[NSThread currentThread]);
        }];
        
        
        NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"+++++++5+%@+++",[NSThread currentThread]);
        }];
        
        //设置监听 |当op4任务结束的时候会执行block中的代码
        //completionBlock 在子线程中执行
        op4.completionBlock = ^{
            NSLog(@"我已经被下载完了,快点来看我吧--%@",[NSThread currentThread]);
        };
        
        //设置依赖
        //5-4-3-2-1
        [op1 addDependency:op2];    //必须要等任务2执行完毕才能执行任务1
        //[op2 addDependency:op1];  !!! 不能设置循环以来
        
        [op2 addDependency:op3];
        [op3 addDependency:op4];
        [op4 addDependency:op5];
        
        //03 把操作添加到队列
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        [queue addOperation:op4];
        [queue2 addOperation:op5];

    输出结果:

        5)设置最大并发数

    /1.创建队列
                NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
                //2.设置最大并发数
                //注意点:该属性需要在任务添加到队列中之前进行设置
                //该属性控制队列是串行执行还是并发执行
                //如果最大并发数等于1,那么该队列是串行的,如果大于1那么是并行的
                //系统的最大并发数有个默认的值,为-1,如果该属性设置为0,那么不会执行任何任务
            queue.maxConcurrentOperationCount = 2;

        6)暂停和恢复以及取消

    //设置暂停和恢复
                //suspended设置为YES表示暂停,suspended设置为NO表示恢复
                //暂停表示不继续执行队列中的下一个任务,暂停操作是可以恢复的
                if (self.queue.isSuspended) {
                    self.queue.suspended = NO;
                }else
                {
                    self.queue.suspended = YES;
                }
    
                //取消队列里面的所有操作
                //取消之后,当前正在执行的操作的下一个操作将不再执行,而且永远都不在执行,就像后面的所有任务都从队列里面移除了一样
                //取消操作是不可以恢复的
                [self.queue cancelAllOperations];

    最后关于GCD 和 NSOperation的对比

     1)GCD是纯C语言的API,而操作队列则是Object-C的对象。
     2)在GCD中,任务用块(block)来表示,而块是个轻量级的数据结构;相反操作队列中的『操作』NSOperation则是个更加重量级的Object-C对象。
     3)具体该使用GCD还是使用NSOperation需要看具体的情况
    
    NSOperation和NSOperationQueue相对GCD的好处有:
        1)NSOperationQueue可以方便的调用cancel方法来取消某个操作,而GCD中的任务是无法被取消的(安排好任务之后就不管了)。
        2)NSOperation可以方便的指定操作间的依赖关系。
        3)NSOperation可以通过KVO提供对NSOperation对象的精细控制(如监听当前操作是否被取消或是否已经完成等)
        4)NSOperation可以方便的指定操作优先级。操作优先级表示此操作与队列中其它操作之间的优先关系,优先级高的操作先执行,优先级低的后执行。
        5)通过自定义NSOperation的子类可以实现操作重用,

    附:

    最近拜读了文顶顶大牛的博客,确实牛,强烈推荐......

  • 相关阅读:
    什么是SQLCLR与使用
    SQL Server中使用正则表达式
    YUV格式
    Android官方开发文档Training系列课程中文版:手势处理之ViewGroup的事件管理
    Android中利用5.0系统屏幕录制UI漏洞骗取应用录制屏幕授权
    Android解析编译之后的所有文件(so,dex,xml,arsc)格式
    PageRank 算法--从原理到实现
    机器人视觉初级系列
    深入解析 iOS 开源项目
    微信热补丁 Tinker 的实践演进之路
  • 原文地址:https://www.cnblogs.com/muzichenyu/p/6034225.html
Copyright © 2011-2022 走看看