多线程-Operation

2018-03-27  本文已影响9人  你weixiao的时候很美

多线程之Operation

本文参考 reywenderlich 的一篇文章和demo介绍自定义operation 文章地址
本文参考简书上的一篇总结文章 文章地址

1. Operation 和 OperationQueue 介绍

2. 基本概念

3. NSInvocationOperation使用方法

-(void)testInvocationOperation{
    NSInvocationOperation* operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(invocationTask) object:nil];
//    [operation  start];              //不添加到queue中,直接调用start,会直接在当前线程完成操作。
    
    NSOperationQueue* queue =[[NSOperationQueue  alloc]init];
    [queue addOperation:operation];       // 添加到自定义queue中会在子线程完成操作。
}
-(void)invocationTask{
    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"%@",[NSThread currentThread]);
}

4.BlockOperation用法

-(void)testBlockOperation{
    NSBlockOperation* blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2.0];
        NSLog(@"%@",[NSThread currentThread]);  //打印当前下线程
    }];
    
    [blockOperation start];
}

和上边 NSInvocationOperation 使用一样,在operation不添加到queue的情况下,代码是在主线程中调用的,所以打印结果为主线程。如果在其他线程中执行操作,则打印结果为其他线程。

-(void)testBlockOperationAddExecutionBlockMethod{
    // 1.创建 NSBlockOperation 对象
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
    }];
    
    op.completionBlock = ^{
        NSLog(@"完成----%@",[NSThread currentThread]);
    };
    
    // 2.添加额外的操作
    [op addExecutionBlock:^{
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
    }];
    [op addExecutionBlock:^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3---%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"4---%@", [NSThread currentThread]);
    }];
    
    [op start];
}
屏幕快照 2018-03-27 上午11.28.59.png

添加的操作多的话,blockOperationWithBlock: 中的操作也可能会在其他线程(非当前线程)中执行,这是由系统决定的,并不是说添加到 blockOperationWithBlock: 中的操作一定会在当前线程中执行

5. 自定义Operation的使用

@implementation CustomOperation  // 继承自NSOperation

-(void)main{
    if (self.isCancelled) {
        return;
    }
    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"%@",[NSThread currentThread]);
}
-(void)testCustomOperation{      // 直接start 或者加入到queue中
    CustomOperation* customOperation = [[CustomOperation alloc]init];
    [customOperation start];
}

6. OperationQueue 的使用

6.1 创建队列
// 主队列获取
    NSOperationQueue * mainQueue = [NSOperationQueue mainQueue];
// 自定义队列的创建
    NSOperationQueue* customQueue = [[NSOperationQueue alloc]init];
6.2 将Operation添加到自定义的队列,开启多线程,进行并发执行。
-(void)testQueueAddOperation{
    
    NSInvocationOperation* invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(invocationTask) object:nil];
    
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"blockOperation---%@", [NSThread currentThread]); // 打印当前线程
    }];
    
    [self.customQueue addOperation:invocationOperation];
    [self.customQueue addOperation:blockOperation];
}
 //直接通过block添加Operation。
    [self.customQueue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"block---%@", [NSThread currentThread]); // 打印当前线程
    }];
6.3 使用OperationQueue控制串行和并行

这里的 maxConcurrentOperationCount 控制的并不是并发线程的数量, 而是一个队列中并发的操作的数量。

当最大并发操作数为1时,操作是按顺序串行执行的,并且一个操作完成之后,下一个操作才开始执行。当最大操作并发数为2时,操作是并发执行的,可以同时执行两个操作。而开启线程数量是由系统决定的,不需要我们来管理。

6.4 Operation操作的依赖

NSOperationQueue 最吸引人的地方是它能添加操作之间的依赖关系。通过操作依赖,我们可以很方便的控制操作之间的执行先后顺序, 有3个接口可以管理和查看依赖。

-(void)testDependency{
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
    }];
    
    [op1 addDependency:op2];
    
    [self.customQueue addOperation:op1];
    [self.customQueue addOperation:op2];
}

无论运行几次都是op2先执行

6.5 Operation操作的优先级

Operation提供 queuePriority属性。 适用于同一个队列中的操作。默认优先级是normal。

//系统定义的 enum
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};
6.6 NSOperationQueue 线程间的通信

operationQueue 使用 [NSOperationQueue mainQueue]addOperationWithBlock: 方法来使子线程和主线程通讯。

//1.
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        //1.1 耗时操作
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        // 1.2回到主线程
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:2]; 
            NSLog(@"main---%@", [NSThread currentThread]); // 打印当前线程
        }];
    }];
    //2.
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        //2.2
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
    }];
    //3.
    [op1 addDependency:op2];
    //4.
    op1.completionBlock = ^{
        //4.1
       NSLog(@"op1完成---%@", [NSThread currentThread]); // 打印当前线程
    };
    //5.
    [self.customQueue addOperation:op1];
    [self.customQueue addOperation:op2];
}
屏幕快照 2018-03-27 下午4.15.10.png

假如上边的一段代码是在主线程下执行的。 那么按照时间的执行顺序是
主线程先执行1. 创建一个带block的op1 -> 主线程执行2. 创建一个带block的op2 -> 主线程执行第三句话3. 添加了依赖 -> 主线程执行第四局4. 给op1 设置了一个block的属性 -> 主线程执行第五句话5. 将op1和op2添加到queue中。 此时某个子线程(这里是子线程3)开始执行2.2 的Op2的block中的操作,睡了2秒,打印 -> 完成以后某个子线程(这里还是子线程3)开始执行op1中的操作1.2,睡了两秒,然后打印。然后执行的操作是 给主队列添加了一个 操作的block,然后op1完成了 -> 然后某个子线程执行完成op1的操作4.1 与此同时,因为主队列因为加入了操作1.2 里的操作。主线程此时执行1.2里的操作。入下图所示

屏幕快照 2018-03-27 下午5.06.28.png

7. 线程安全

-(void)testSafe{
    self.ticketSurplusCount = 50;
    // 1.创建 queue1
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    queue1.maxConcurrentOperationCount = 1;
    // 2.创建 queue2
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    queue2.maxConcurrentOperationCount = 1;
    
    // 3.创建操作op1
    __weak typeof(self) weakSelf = self;
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [weakSelf saleTicketNotSafe];
//        [weakSelf saleTicketSafe];     //加lock,线程安全
    }];
    // 4.创建操作op2
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [weakSelf saleTicketNotSafe];
//        [weakSelf saleTicketSafe];     //加lock,线程安全
     }];
    // 5.添加操作
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
}

- (void)saleTicketNotSafe {
    while (1) {
        
        if (self.ticketSurplusCount > 0) {
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余数:%ld 线程:%@", self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        } else {
            NSLog(@"所有数已经减完");
            break;
        }
    }
}

- (void)saleTicketSafe {
    while (1) {
        
        // 加锁
        [self.lock lock];
        if (self.ticketSurplusCount > 0) {
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余数:%ld 线程:%@", self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        }
        // 解锁
        [self.lock unlock];
        if (self.ticketSurplusCount <= 0) {
            NSLog(@"所有数已经减完");
            break;
        }
    }
}

8. 常见属性和api

8.1 Operation的属性和方法

// 取消操作
-(void)cancel 取消操作,实质是 标记Operation的isCancelled

//判断状态

// 操作
-(void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock。

(void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成

(void)removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作 op 的依赖

@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组

8.2 OperationQueue的常见属性和操作

// 取消,暂停,恢复队列
(void)cancelAllOperations; 可以取消队列的所有操作。

(BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。

(void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列

// 操作和获取Operation
(void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation 类型操作对象。

(NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。

(NSUInteger)operationCount; 当前队列中的操作数

// 获取队列

上一篇 下一篇

猜你喜欢

热点阅读