iOS中的多线程(关于NSOperationQueue)

2,630 阅读5分钟

「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

关于NSOperationQueue

NSOperationQueue 一共有两种队列:

  • 主队列

    //获取主队列 
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    
  • 自定义队列:通过设置最大并发数属性来控制任务是并发执行还是串行执行

    • 并发执行
    • 串行执行
    //获取自定义队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    

NSInvocationOperation 和 NSOperationQueue 组合

示例:

//创建队列(创建的队列中的任务默认是异步执行的)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//创建任务
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];

//任务添加队列
[queue addOperation:op1];
[queue addOperation:op2];


- (void)run1{
    NSLog(@"1-- %@",[NSThread currentThread]);
}
- (void)run2{
    NSLog(@"2-- %@",[NSThread currentThread]);
}

log:

Snip20211109_11.png


NSInvocationOperation 和 NSBlockOperation 组合

示例:

//创建队列(创建的队列中的任务默认是异步执行的)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//创建任务
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"1-- %@",[NSThread currentThread]);
}];

NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"2-- %@",[NSThread currentThread]);
}];

//添加额外任务
[op1 addExecutionBlock:^{
    NSLog(@"3-- %@",[NSThread currentThread]);
}];

//添加额外任务
[op1 addExecutionBlock:^{
    NSLog(@"4-- %@",[NSThread currentThread]);
}];

//任务添加队列
[queue addOperation:op1];
[queue addOperation:op2];

//可以直接创建任务到队列中去
[queue addOperationWithBlock:^{
    NSLog(@"5-- %@",[NSThread currentThread]);
}];

log:(任务都是并发执行的) Snip20211109_13.png


NSInvocationOperation 和 自定义NSOperation 组合

示例:

//创建队列(创建的队列中的任务默认是异步执行的)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//创建任务需要继承NSOperation,执行的操作需要放在这个自定义类的main中
Operation *op1 = [[Operation alloc]init];

//创建任务需要继承NSOperation,执行的操作需要放在这个自定义类的main中
Operation *op2 = [[Operation alloc]init];

//任务添加队列
[queue addOperation:op1];
[queue addOperation:op2];

log: Snip20211109_14.png


NSOperation其它用法

设置最大并发数(控制任务并发和串行)

  • 最大并发数:队列在同一时间中最多有多少个任务可以执行
  • 通过属性 maxConcurrentOperationCount 设置最大并发数量
    • maxConcurrentOperationCount = -1 ,不进行限制,并发执行
    • maxConcurrentOperationCount = 1 ,串行队列,串行执行
    • maxConcurrentOperationCount > 1 ,并发队列,并发执行
    • maxConcurrentOperationCount = 0 ,不会执行

注:

  • 同一时间最多有多少个任务可以执行
  • 串行执行并不是只开一条线程,它只是线程同步,区别串行还是并行,不是看它开了多少条线程,而是看任务的执行方式,是有序的还是无序的

示例:(设置最大并发数为1,队列为串行执行任务)

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//最大并发数设置为1
queue.maxConcurrentOperationCount  = 1;

[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"1-- %@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"2-- %@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"3-- %@",[NSThread currentThread]);
}];

log:(线程为多条,但是属于线程同步) Snip20211110_20.png

示例:(设置最大并发数为2,队列为串行执行任务)

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//最大并发数设置为1
queue.maxConcurrentOperationCount  = 2;

[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"1-- %@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"2-- %@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"3-- %@",[NSThread currentThread]);
}];

log:(线程为多条,异步执行,不管加入队列有多少操作,实际队列并发数为2) Snip20211110_21.png

队列的暂停和恢复以及取消

  • 暂停和恢复队列

    • 暂停表示不继续执行队列中的下一个任务,暂停是可以恢复的
    • 队列中的任务也是有状态的,分为:已经执行完毕、正在执行、排队等待状态
    • 不能暂停当前正在处于执行状态的任务
    //暂停
    queue.suspended = YES;
    //恢复
    queue.suspended = NO;
    
  • 取消队列的所有操作

    • 取消队列里所有的操作
    • 取消之后,当前正在执行的操作的下一个操作将不再执行
    • 取消操作是不可恢复的
    • 已经取消操作,再次启动需要重新加入队列
    [queue cancelAllOperations]
    

示例:

创建队列并执行

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.queue = [[NSOperationQueue alloc]init];
    self.queue.maxConcurrentOperationCount = 1;
    
    [self.queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"-1--%@",[NSThread currentThread]);
    }];
    
    [self.queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"-2--%@",[NSThread currentThread]);
    }];
    
    [self.queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"-3--%@",[NSThread currentThread]);
    }];
    
    [self.queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"-4--%@",[NSThread currentThread]);
    }];
}

//暂停和恢复队列
- (void)suspendedClick:(id)sender {
    if(self.queue.suspended){
        //恢复队列,继续执行
        self.queue.suspended  = NO;
    }else{
        //挂起(暂停队列)
        self.queue.suspended  = YES;
    }
}

//取消队列
- (void)cancelClick:(id)sender {
    [self.queue cancelAllOperations];
}

设置队列操作依赖

  • NSOperation 之间可以设置依赖来保证执行顺序,比如A执行完之后再执行B,就可以使用设置依赖
  • 设置依赖
    [op1 addDependency:op3];
    
  • 取消依赖
    [op1 removeDependency:op3];
    
  • 不可以循环依赖
    [op1 addDependency:op3];
    [op3 addDependency:op1];
    
  • 设置监听:监听一个操作的执行完毕
    [op2 setCompletionBlock:^{
        NSLog(@"2执行完成");
    }];
    

示例:

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"1---%@",[NSThread currentThread]);
}];

NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"2---%@",[NSThread currentThread]);
}];

NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"3---%@",[NSThread currentThread]);
}];

//设置依赖(op1和op3执行完之后才执行2)
[op1 addDependency:op3];
[op2 addDependency:op1];

[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];

//监听一个操作的执行完成
[op2 setCompletionBlock:^{
    NSLog(@"2执行完成");
}];

log:(依赖可知优先级:op3 > op1 > op2,监听的操作不一定和被监听的操作同一个线程,都是异步的,只是op3执行结束,肯定会执行监听的操作) Snip20211110_23.png

监控NSOperation对象的属性

  • isExecuting 表示任务正在执行中
  • isFinished 表示任务已经执行完成,被取消也算执行完成
  • isCancelled 表示任务已经取消执行
  • isAsynchronous 表示任务是并发还是同步执行
  • isReady 表示任务是并发还是同步执行

队列里的优先级

  • iOS8以前,NSOperation 通过设置 queuePriority 属性来设置优先级
  • iOS 8.0后,NSOperation 通过设置 qualityOfService 来设置优先级
  • 优先级高的先执行,低的后执行
//iOS8以前的优先级(queuePriority)
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

//iOS8以后的优先级(qualityOfService)
typedef NS_ENUM(NSInteger, NSQualityOfService) {
    NSQualityOfServiceUserInteractive = 0x21,//最高优先级,用于用户交互事件
    NSQualityOfServiceUserInitiated = 0x19,//次高优先级,用于用户需要马上执行的事件
    NSQualityOfServiceUtility = 0x11,//默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
    NSQualityOfServiceBackground = 0x09,//普通优先级,用于普通任务
    NSQualityOfServiceDefault = -1//最低优先级,用于不重要的任务
}
NSOperationQueue *queue = [[NSOperationQueue alloc]init];

NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"down1---%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"down2---%@",[NSThread currentThread]);
    
}];

NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"down3---%@",[NSThread currentThread]);
    
}];

//设置优先级
op3.qualityOfService = NSQualityOfServiceUserInteractive;

[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];

log: Snip20211110_24.png