美文网首页活好
ios - 多线程之十一:NSOperation

ios - 多线程之十一:NSOperation

作者: 乐意先生 | 来源:发表于2017-05-09 14:58 被阅读45次

    NSOperation 是抽象类,所以要用子类来进行线程使用;目前是三种方式:
    1:NSInvocationOperation
    2:NSBlockOperation
    3:创建子类继承自 NSOperation

    1:NSInvocationOperation

    NSInvocationOperation : 可以理解为是一个为 @selector 包装上任务特性,能够在子线程中执行;

    注意事项:
    1:单独使用 NSInvocationOperation 不会开启新线程,会在主线程中执行,造成主线程堵塞;
    2:创建 NSInvocationOperation 对象之后需要手动开启 start ;
    3:此时要结合 NSOperationQueue 组合使用;(衍生出一个问题:GCD , operation 都体现出了线程队列的概念,队列对多线程来说为什么如此重要?)
    4:在和 NSOperationQueue 组合使用时,不用再通过 start 进行开启;
    5:NSInvocationOperation 的使用一定要结合着 NSOperationQueue

    代码展示:

    NSInvocationOperation *InvocationOperationA = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
    InvocationOperationA.name = @"operation - invocationA";
    
    NSInvocationOperation *InvocationOperationB = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
    InvocationOperationB.name = @"operation - invocationB";
    
    NSInvocationOperation *InvocationOperationC = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
    InvocationOperationC.name = @"operation - invocationC";
    
    NSInvocationOperation *InvocationOperationD = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
    InvocationOperationD.name = @"operation - invocationD";
    
    NSInvocationOperation *InvocationOperationE = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
    InvocationOperationE.name = @"operation - invocationE";
    
    
    //启动任务 :NSInvocationOperation 在和 NSOperationQueue 组合使用时,不需要使用 start
    //    [InvocationOperationA start];
    //    [InvocationOperationB start];
    //    [InvocationOperationC start];
    //    [InvocationOperationD start];
    
    //NSOperationQueue - 线程队列
    NSOperationQueue *operQueue = [[NSOperationQueue alloc] init];
    
    operQueue.name = @"oper_Queue";   //设置队列名称
    
    [operQueue setMaxConcurrentOperationCount:2]; //设置队列中允许的最大线程并发数
    
    
    //判断任务的执行状态
    if (InvocationOperationA.isExecuting) {
        NSLog(@"InvocationOperationA 是执行");
    }else{
        NSLog(@"InvocationOperationA 还未执行");
    }
    
    
    /*
     NSOperationQueue - 两种在队列中添加事务的方式;
        1 : addOperation
        2 : addOperationWithBlock
     */
    //在队列中添加要执行的操作或者任务;
    [operQueue addOperation:InvocationOperationA];
    [operQueue addOperation:InvocationOperationB];
    [operQueue addOperation:InvocationOperationC];
    [operQueue addOperation:InvocationOperationD];
    [operQueue addOperation:InvocationOperationE];
    

    2:NSBlockOperation

    NSBlockOperation 两种方式存放任务代码

    1:类方法 - blockOperationWithBlock
    ** 2:实例方法 - addExecutionBlock**

    注意事项:
    1:单独使用 NSBlockOperation 设置一个 addExecutionBlock 不会开启新线程,会在主线程中执行,造成主线程堵塞;
    2:单独使用 NSBlockOperation 中的 addExecutionBlock 方式设置任务时候,任务除在主线程执行之外,还会开启新的子线程来执行其他任务。但还是会造成主线程的堵塞;
    3:创建 NSBlockOperation 对象之后需要手动开启 start ;
    4:此时要结合 NSOperationQueue 组合使用;(衍生出一个问题:GCD , operation 都体现出了线程队列的概念,队列对多线程来说为什么如此重要?)
    5:在和 NSOperationQueue 组合使用时,不用再通过 start 进行开启;

    实现组合两种: 都不会主线程的堵塞

    第一种 :
    1 :NSBlockOperation 实例化一个对象;
    2 :使用 addExecutionBlock 添加要执行的任务代码;可以添加多个;
    3 :创建 NSOperationQueue 实例
    4 :在 NSOperationQueue 实例中添加 NSBlockOperation 实例
    5 :不要再使用 start 进行启动了

    优缺点:
    (优点)1:不用创建很多 NSBlockOperation 对象,在一个对象添加多个任务的 block 代码;
    (优点)2:会根据 NSBlockOperation 中添加的 block 块数量开启相应多的子线程执行任务;(需要思考的问题:为什么会根据 block 开启子线程?是不是和 block 的实现原理有关,或者和其特殊属性有关?)
    (缺点)3:通过 NSOperationQueue 的函数 setMaxConcurrentOperationCount 控制并发数是不生效的;

    第二种 :
    1 :NSBlockOperation 实例化多个对象;
    2 :在每个 NSBlockOperation 实例对象中设置任务的 block 代码;
    3 :创建 NSOperationQueue 实例
    4 :在 NSOperationQueue 实例中添加所有要使用的 NSBlockOperation 实例
    5 :不要再使用 start 进行启动了

    优缺点:
    (优点)1:可以通过 NSOperationQueue 的函数 setMaxConcurrentOperationCount 控制并发数;
    (优点)2:会根据 NSBlockOperation 的 setMaxConcurrentOperationCount 设置数量开启相应多的子线程执行任务;
    (缺点)3:需要为每一个任务的 block 块创建一个 NSBlockOperation 实例;

    代码展示:

    [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i <= 10 ; i ++) {
            sleep(1);
            NSLog(@"类方法 blockOperation - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
        }
    }];
    
    
    NSBlockOperation *blockOperation = [[NSBlockOperation alloc] init];
    blockOperation.name = @"blockOperation";
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i <= 10 ; i ++) {
            sleep(1);
            NSLog(@"blockOperation - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 20; i <= 30 ; i ++) {
            sleep(1);
            NSLog(@"blockOperation - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 30; i <= 40 ; i ++) {
            sleep(1);
            NSLog(@"blockOperation - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
        }
    }];
    
    //    [blockOperation start];
    
    
    
    
    NSBlockOperation *blockOperationA = [[NSBlockOperation alloc] init];
    blockOperationA.name = @"blockOperationA";
    [blockOperationA addExecutionBlock:^{
        for (int i = 0; i <= 10 ; i ++) {
            sleep(1);
            NSLog(@"blockOperationA - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
        }
    }];
    NSBlockOperation *blockOperationB = [[NSBlockOperation alloc] init];
    blockOperationB.name = @"blockOperationB";
    [blockOperationB addExecutionBlock:^{
        for (int i = 0; i <= 10 ; i ++) {
            sleep(1);
            NSLog(@"blockOperationB - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
        }
    }];
    NSBlockOperation *blockOperationC = [[NSBlockOperation alloc] init];
    blockOperationC.name = @"blockOperationC";
    [blockOperationC addExecutionBlock:^{
        for (int i = 0; i <= 10 ; i ++) {
            sleep(1);
            NSLog(@"blockOperationC - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
        }
    }];
    
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    [operationQueue setMaxConcurrentOperationCount:2];
    //方式一
    [operationQueue addOperation:blockOperation];
    
    
    
    //方式二
    //    [operationQueue addOperation:blockOperationA];
    //    [operationQueue addOperation:blockOperationB];
    //    [operationQueue addOperation:blockOperationC];
    

    3:创建子类继承自 NSOperation

    //NSOperation - 自定义线程 - CustomOperation
    *
    个人解读:
    所谓的自定义线程就是在继承 NSOperation 之后,在类内部设置好该类要执行的特定任务,也就是说自定线程,自定义的部分就是明确该线程要执行的操作是什么。
    *

    1:创建文件

    自定义线程

    2:在.m文件重写 main 方法

      - (void) main {
    
    //NSOperation - 自定义使用实现子线程操作 - 同步任务
    
    NSLog(@"线程名字 = %@",self.name);
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"自定义线程 %@——%d",self.name,i);
    }
    }
    

    3:如果在自定义 NSOperation 时 自定义使用实现子线程操作 - 异步任务,

    NSLog(@"开始任务");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        if (self.isCancelled) {
            return ;
        }
        for (int i = 0; i <= 10 ; i ++) {
            sleep(1);
            NSLog(@"自定义线程 ——%d",i);
        }
        NSLog(@"循环结束");
        self.over = YES;
    });
    NSLog(@"结束任务");
    

    执行结果:

    自定义线程,异步任务

    该如何解决:

    NSLog(@"开始任务");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        if (self.isCancelled) {
            return ;
        }
        for (int i = 0; i <= 10 ; i ++) {
            sleep(1);
            NSLog(@"自定义线程 ——%d",i);
        }
        NSLog(@"循环结束");
        self.over = YES;
    });
    
    //解决办法 : 使用 NSRunLoop 进行循环执行
    /*
        通过 NSRunLoop 的控制线程,等待异步任务完成之后再让该线程执行完成。
     */
    while (!self.over && !self.isCancelled) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
    
    
    NSLog(@"结束任务");
    

    NSOperation有一个很大的便捷就是处理线程依赖:

    CustomOperation *operation1 = [[CustomOperation alloc] initWithName:@"自定义线程一"];
    CustomOperation *operation2 = [[CustomOperation alloc] initWithName:@"自定义线程二"];
    CustomOperation *operation3 = [[CustomOperation alloc] initWithName:@"自定义线程三"];
    CustomOperation *operation4 = [[CustomOperation alloc] initWithName:@"自定义线程四"];
    CustomOperation *operation5 = [[CustomOperation alloc] initWithName:@"自定义线程五"];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.name = @"自定义线程的所在队列";
    [queue setMaxConcurrentOperationCount:2];
    
    
    
    //NSOperation - 线程依赖 Dependency(依赖)
    /*
     添加上依赖之后,两个线程就有了依赖关系,举例说明, A 设置了依赖 B ,带代码执行阶段时, 只有当 B 执行完之后才会执行 A;
     
     应用场景:
     在实际开发过程中,任务3 之后再任务1和任务2完成之后才能执行就可以使用依赖关系
     
     注意事项:
     再设置依赖关系的时候要注意不要添加依赖关系形成闭环,出现互相依赖的情况,会导致程序不执行;
     */
    [operation1 addDependency:operation5];
    [operation5 addDependency:operation2];
    [operation2 addDependency:operation4];
    [operation4 addDependency:operation3];
    //    [operation3 addDependency:operation1];  这一点一定要注意,千万要捋顺彼此的依赖关系
    
    //一对多的依赖关系
    /*
        只有当多个有依赖的任务执行完成之后才会执行
     */
    //    [operation1 addDependency:operation5];
    //    [operation1 addDependency:operation2];
    //    [operation2 addDependency:operation4];
    //    [operation4 addDependency:operation3];
    
    
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
    [queue addOperation:operation4];
    [queue addOperation:operation5];

    相关文章

      网友评论

        本文标题:ios - 多线程之十一:NSOperation

        本文链接:https://www.haomeiwen.com/subject/wanbtxtx.html