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的子类可以实现操作重用,

附:

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

原文地址:https://www.cnblogs.com/muzichenyu/p/6034225.html