iOS 多线程---NSOperation

2020-09-08  本文已影响0人  iOS程序媛ing

NSOperation、NSOperationQueue是对GCD的进一步封装,比GCD简单易用、更易读;

为什么使用NSOperation
1、NSOperation可以设置任务之间的依赖关系;
2、NSOperation可以设置任务的优先级;
3、NSOperaion可以获取任务当前的状态isFinished(已完成)、isCancelled(已取消)、isExecuteing(正在执行)

1、NSOperaion、NSOperationQueue操作和操作队列

1.1 NSOperation操作

通过NSOperation的子类NSBlockOperation和NSInvotionOperaion或者自定义创建操作
需要注意的是,如果操作没有添加到操作队列中,需要调用start方法执行任务,如果添加到队列中,不需要调用start方法

 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        
    }];
    [operation start];
 NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocation) object:nil];
    [operation2 start];
2、NSOperationQueue

NSOperationQueue通过设置最大并发数,来确定是串行队列还是并发队列
(1)默认情况下最大并发数是-1,不限制开辟线程数;
(2)当最大并发数为1时,为串行队列;
(3)当最大并发数大于1时,为并发队列;
maxConcurrentOperationCount = 0,不执行。默认是maxConcurrentOperationCount = -1,异步队列

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 0;

2.1将操作添加到操作队列中
将操作添加到操作队列中有两种方式
第一种addOperation,创建操作后,将操作添加到队列

[queue addOperation:operation];

第二种addOperationWithBlock,直接添加任务,不需要创建操作

[queue addOperationWithBlock:^{
        NSLog(@"执行操作");
    }];
3、创建依赖

下面表示operation依赖operation2操作,operation2操作完成后operation操作才会执行
需要注意的是,先添加依赖关系,再将操作添加到操作队列中,否则依赖关系不起作用

 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        
    }];
  
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocation) object:nil];
    
 [operation addDependency:operation2];
    [queue addOperation:operation];
    [queue addOperation:operation2];
   
4、优先级

NSOperaion可以通过设置优先级,改变操作执行顺序,默认情况下,操作的优先级是NSOperationQueuePriorityNormal, 我们可以通过setQueuePriority方法设置操作的优先级

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

如果一个操作队列中既设置了优先级,也设置了依赖关系,有限执行已经准备就绪的操作,如果当前有op1、op2、op3、op4四个操作添加到队列中,op2依赖于op1,op3依赖于op2,op1和op4都没有依赖,那么op1和op4处于准备就绪状态,op2和op3处于未准备就绪状态,如果op4的优先级高于op1,当最大并发数为1的情况下那么执行顺序是op4、op1、op2、op3;如果op2或者op3的优先级高于op4,执行顺序还是如此。如果最大并发数不为1,那么执行顺序为op4、op1、op2、op3或op1、op4、op2、op3.

5、线程间的通信
 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
     NSLog(@"1===%@", [NSThread currentThread]);
     [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"回到主线程刷新界面");
        }];
    }];
 [queue addOperation:operation1];
6、线程安全

6.1NSOperation、NSOperationQueue 非线程安全
下面,我们模拟火车票售卖的方式,实现 NSOperation 线程安全和解决线程同步问题。
场景:总共有100张火车票,有两个售卖火车票的窗口,一个是广东火车票售卖窗口,另一个是广西火车票售卖窗口。两个窗口同时售卖火车票,卖完为止。

/**
 * 非线程安全:不使用 NSLock
 * 初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
 */
- (void)initTicketStatusNotSave {
    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程

    self.ticketSurplusCount = 50;

    // 1.创建 queue1,queue1 代表广东火车票售卖窗口
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    queue1.maxConcurrentOperationCount = 1;

    // 2.创建 queue2,queue2 代表广西火车票售卖窗口
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    queue2.maxConcurrentOperationCount = 1;

    // 3.创建卖票操作 op1
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [self saleTicketNotSafe];
    }];

    // 4.创建卖票操作 op2
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [self saleTicketNotSafe];
    }];

    // 5.添加操作,开始卖票
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
}

/**
 * 售卖火车票(非线程安全)
 */
- (void)saleTicketNotSafe {
    while (1) {

        if (self.ticketSurplusCount > 0) {
            //如果还有票,继续售卖
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        } else {
            NSLog(@"所有火车票均已售完");
            break;
        }
    }
}

6.2线程安全,加线程锁(使读写操作在同一线程操作,才能保证线程安全。atomic只是保证读写安全)

- (void)saleTicketNotSafe {
    while (1) {
     [self.lock lock];
        if (self.ticketSurplusCount > 0) {
            //如果还有票,继续售卖
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        } else {
            NSLog(@"所有火车票均已售完");
            break;
        }
      [self.lock unlock];
    }
}
上一篇 下一篇

猜你喜欢

热点阅读