iOS多线程之NSOperationQueue-依赖、并发数、优先级、自定义Operation等最全的使用总结

简介: iOS多线程之NSOperationQueue-依赖、并发数、优先级、自定义Operation等最全的使用总结

1. 简介

NSOperation(任务): 通过start方法开始执行任务、默认是同步执行的

NSOperationQueue(队列): 将NSOperation添加到队列中执行(主队列除外),是异步执行的


2. NSOperation创建

1) NSOperation一般不直接使用,而是使用它的子类NSInvocationOperation和NSBlockOperation

2) 可以看到在没有添加到对列的时候都是在当前线程执行的

文末附工程源码



- (void)createOperation {
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    [op start];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    [blockOp start];
}
- (void)createOperation {
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    [op start];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    [blockOp start];
}

运行结果:



2021-04-11 11:12:24.522251+0800 OperationQueue[1485:43624] obj - <NSThread: 0x600003a0c900>{number = 1, name = main}
2021-04-11 11:12:24.522431+0800 OperationQueue[1485:43624] block - <NSThread: 0x600003a0c900>{number = 1, name = main}


3. 将NSOperation添加到Queue中

1)可以利用[queue addOperation:op]将NSOperation添加到NSOperationQueue中

2)也可以利用[queue addOperations:@[op, blockOp] waitUntilFinished:NO]将NSOperation添加到NSOperationQueue中

3注意一不可用重复添加同一个NSOperation到NSOperationQueue中,否则会报如下错误

- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
//    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
//    [op start];
//    [op cancel];
}
- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
//    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
//    [op start];
//    [op cancel];
}

运行结果:



*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperations:waitUntilFinished:]: 2 (of 2) operations are finished, executing, or already in a queue, and cannot be enqueued'
terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperations:waitUntilFinished:]: 2 (of 2) operations are finished, executing, or already in a queue, and cannot be enqueued'
terminating with uncaught exception of type NSException

4注意二不可将NSOperation调用start或者cancel之后再添加到NSOperationQueue中,否则会报如下错误

- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
//    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
//    [op start];
//    [op cancel];
}

运行结果:



*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued'
terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued'
terminating with uncaught exception of type NSException

5注意三不可将NSOperation添加NSOperationQueue之后,再次调用star或者cancelt去修改状态,否则会报如下错误

- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
//    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
//    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
    [op start];
//    [op cancel];
}
- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
//    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
//    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
    [op start];
//    [op cancel];
}

运行结果:



*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSInvocationOperation start]: something is trying to start the receiver simultaneously from more than one thread'
terminating with uncaught exception of type NSException


4. 添加依赖关系

1)当需要某个NSOperation对象依赖于其它NSOperation对象时,可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作的时候,当前NSOperation对象才会开始执行操作,也可以通过removeDependency方法来删除依赖对象

2)不添加依赖关系应该先打印block再打印obj见上方日志,设置依赖后见下方代码说明

3)设置依赖关系,必须在addOperation之前设置,否则没有效果

4)可以相互依赖,否则都不会执行


- (void)addDependency {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    [op addDependency:blockOp];//添加依赖
//    [blockOp addDependency:op];//相互依赖
//    [op removeDependency:blockOp];//移除依赖
    [queue addOperation:op];
    [queue addOperation:blockOp];
}
- (void)addDependency {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    [op addDependency:blockOp];//添加依赖
//    [blockOp addDependency:op];//相互依赖
//    [op removeDependency:blockOp];//移除依赖
    [queue addOperation:op];
    [queue addOperation:blockOp];
}

运行结果:



2021-04-11 11:29:47.303916+0800 OperationQueue[1586:56394] block - <NSThread: 0x600003ead180>{number = 5, name = (null)}
2021-04-11 11:29:47.304047+0800 OperationQueue[1586:56394] obj - <NSThread: 0x600003ead180>{number = 5, name = (null)}
2021-04-11 11:29:47.303916+0800 OperationQueue[1586:56394] block - <NSThread: 0x600003ead180>{number = 5, name = (null)}
2021-04-11 11:29:47.304047+0800 OperationQueue[1586:56394] obj - <NSThread: 0x600003ead180>{number = 5, name = (null)}


5. 优先级

1)执行顺序规则

不设置依赖关系的前提下:(可通过调试下方代码看执行结果)

(1)如果都不设置优先级、并且内部没有耗时操作,则按照添加顺序执行;

(2)如果都不设置优先级、并且内部有耗时操作,则耗时操作的后执行,没有耗时操作的先执行;

(3)如果设置了优先级、并且内部没有耗时操作,则按照处于ready状态的operation优先级等级的高低执行;

(4)如果设置了优先级、并且内部有耗时操作,则耗时操作的后执行,没有耗时操作的按照处于ready状态的operation优先级等级的高低执行;

2)优先级不能替代依赖关系并且依赖关系高于优先级的设置,在保证依赖关系的情况下才能考虑优先级


- (void)setQueuePriority {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
        sleep(3);
    }];
    [op setQueuePriority:NSOperationQueuePriorityHigh];
    [blockOp setQueuePriority:NSOperationQueuePriorityLow];
    [op addDependency:blockOp];
    [queue addOperation:op];
    [queue addOperation:blockOp];
}
- (void)setQueuePriority {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
        sleep(3);
    }];
    [op setQueuePriority:NSOperationQueuePriorityHigh];
    [blockOp setQueuePriority:NSOperationQueuePriorityLow];
    [op addDependency:blockOp];
    [queue addOperation:op];
    [queue addOperation:blockOp];
}

运行结果:


2021-04-11 11:39:07.960943+0800 OperationQueue[1621:62599] block - <NSThread: 0x600000715640>{number = 3, name = (null)}
2021-04-11 11:39:10.964436+0800 OperationQueue[1621:62599] obj - <NSThread: 0x600000715640>{number = 3, name = (null)}
2021-04-11 11:39:07.960943+0800 OperationQueue[1621:62599] block - <NSThread: 0x600000715640>{number = 3, name = (null)}
2021-04-11 11:39:10.964436+0800 OperationQueue[1621:62599] obj - <NSThread: 0x600000715640>{number = 3, name = (null)}

6. NSOperationQueue的挂起和恢复

可以根据你实际的需求来确定是否要挂起和恢复,挂起或者恢复NSOperationQueue,并不会影响正在执行的NSOperation

    [queue setSuspended:YES];//挂起
    [queue setSuspended:NO];//恢复
    [queue setSuspended:YES];//挂起
    [queue setSuspended:NO];//恢复


7. 等待完成

1) [blockOp waitUntilFinished]等待当前的操作完成后,才会继续向下执行,注意:会阻塞线程

2) [queue waitUntilAllOperationsAreFinished]等待所有的操作完成后,才会继续向下执行,注意:会阻塞线程



[blockOp waitUntilFinished];
[queue waitUntilAllOperationsAreFinished];
[blockOp waitUntilFinished];
[queue waitUntilAllOperationsAreFinished];


8. 取消Operations

取消所有正在执行的操作


[queue cancelAllOperations];// 两个操作都不会执行

9. 并发数

设置单次最大的执行数量,如果多于1个,是完成一个向队列里面追加一个的执行,而不是等待同时执行的最大个数执行完之后再执行后面的操作


queue.maxConcurrentOperationCount = 1;

10. 自定义NSOperation

1)NSInvocationOperation和NSBlockOperation不能满足我们的需求时就需要我们自定义,自定义NSOperation的场景主要是为了我们能更好的控制一个任务,什么情况下开始、取消、完成, 并且可以分离代码的实现

2)实现步骤:(可参考下面代码)

(1实现start方法

(2)重写isAsynchronous,是否为并发任务、根据自己的情况即可

(3)main方法可根据自己的实际需要决定是否实现、一般实现并在里面做自己的事

(4)finished、executing更新任务的状态,用于触发KVO

(5)自己的任务完成后,一定要把当前的任务状态改为完成,否则队列中后面的任务,将永远不会执行



@property(assign, nonatomic, getter=isFinished) BOOL finished;
@property(assign, nonatomic, getter=isExecuting) BOOL executing;
@property(assign, nonatomic, getter=isStart) BOOL start;
@property(assign, nonatomic, getter=isFinished) BOOL finished;
@property(assign, nonatomic, getter=isExecuting) BOOL executing;
@property(assign, nonatomic, getter=isStart) BOOL start;

@implementation CustomOperation
@synthesize finished = _finished, executing = _executing;
- (BOOL)isAsynchronous {
    return NO;
}
- (void)setFinished:(BOOL)finished {
    [self willChangeValueForKey:@"finished"];
    _finished = finished;
    [self didChangeValueForKey:@"finished"];
}
- (void)setExecuting:(BOOL)executing {
    [self willChangeValueForKey:@"executing"];
    _executing = executing;
    [self didChangeValueForKey:@"executing"];
}
- (void)start {
    @synchronized (self) {
        self.start = YES;
        if (self.isCancelled) {
            [self completed];
            return;
        }
        @autoreleasepool {
            [self main];
        }
        self.executing = YES;
    }
}
- (void)main {//在此处做一些更复杂的操作
    NSLog(@"准备开始执行任务啦~ %@", [NSThread currentThread]);
    //模拟3s后执行doSomething
    [self performSelector:@selector(doSomething:) withObject:nil afterDelay:3.0];
    [[NSRunLoop currentRunLoop] run];//此处需要添加到Runloop,子线程默认没有开启,后期文章会具体说明
}
- (void)doSomething:(id)obj {
    NSLog(@"完成了当前的任务!%@", [NSThread currentThread]);
    [self completed];
}
- (void)completed {
    @synchronized (self) {
        self.executing = NO;
        self.finished = YES;
    }
}
- (void)cancel {
    [super cancel];
    if (self.isStart) {
        [self completed];
    }
}
@end
@implementation CustomOperation
@synthesize finished = _finished, executing = _executing;
- (BOOL)isAsynchronous {
    return NO;
}
- (void)setFinished:(BOOL)finished {
    [self willChangeValueForKey:@"finished"];
    _finished = finished;
    [self didChangeValueForKey:@"finished"];
}
- (void)setExecuting:(BOOL)executing {
    [self willChangeValueForKey:@"executing"];
    _executing = executing;
    [self didChangeValueForKey:@"executing"];
}
- (void)start {
    @synchronized (self) {
        self.start = YES;
        if (self.isCancelled) {
            [self completed];
            return;
        }
        @autoreleasepool {
            [self main];
        }
        self.executing = YES;
    }
}
- (void)main {//在此处做一些更复杂的操作
    NSLog(@"准备开始执行任务啦~ %@", [NSThread currentThread]);
    //模拟3s后执行doSomething
    [self performSelector:@selector(doSomething:) withObject:nil afterDelay:3.0];
    [[NSRunLoop currentRunLoop] run];//此处需要添加到Runloop,子线程默认没有开启,后期文章会具体说明
}
- (void)doSomething:(id)obj {
    NSLog(@"完成了当前的任务!%@", [NSThread currentThread]);
    [self completed];
}
- (void)completed {
    @synchronized (self) {
        self.executing = NO;
        self.finished = YES;
    }
}
- (void)cancel {
    [super cancel];
    if (self.isStart) {
        [self completed];
    }
}
@end

运行结果:



2021-04-11 11:48:12.601640+0800 OperationQueue[1671:69134] 准备开始执行任务啦~ <NSThread: 0x600000b58380>{number = 8, name = (null)}2021-04-11 11:48:15.605299+0800 OperationQueue[1671:69134] 完成了当前的任务!<NSThread: 0x600000b58380>{number = 8, name = (null)}

源码:https://github.com/gltwy/public

相关文章
|
1月前
|
算法 Unix Linux
Linux与Qt线程优先级的对应关系:一次全面解析
Linux与Qt线程优先级的对应关系:一次全面解析
23 0
|
6月前
|
并行计算 安全 Java
深入理解Java并发编程:并行与并发、进程与线程、优先级、休眠与让步
深入理解Java并发编程:并行与并发、进程与线程、优先级、休眠与让步
258 0
|
7月前
|
Java 调度
Java线程的优先级
Java线程的优先级
54 0
|
1月前
|
资源调度 算法 Linux
Linux进程/线程的调度机制介绍:详细解析Linux系统中进程/线程的调度优先级规则
Linux进程/线程的调度机制介绍:详细解析Linux系统中进程/线程的调度优先级规则
101 0
|
1月前
|
安全 Java 调度
【C/C++ 线程池设计思路 】设计与实现支持优先级任务的C++线程池 简要介绍
【C/C++ 线程池设计思路 】设计与实现支持优先级任务的C++线程池 简要介绍
44 2
|
1月前
|
监控 Linux 调度
【Linux 应用开发 】Linux 下应用层线程优先级管理解析
【Linux 应用开发 】Linux 下应用层线程优先级管理解析
48 0
|
3月前
|
iOS开发
多线程和异步编程:解释 iOS 中的同步和异步任务的概念。
多线程和异步编程:解释 iOS 中的同步和异步任务的概念。
38 1
|
3月前
|
API 调度 iOS开发
多线程和异步编程:什么是 GCD(Grand Central Dispatch)?如何在 iOS 中使用 GCD?
多线程和异步编程:什么是 GCD(Grand Central Dispatch)?如何在 iOS 中使用 GCD?
28 1
|
6月前
|
算法 安全 调度
[笔记]Windows核心编程《六》线程调度、优先级和关联性
[笔记]Windows核心编程《六》线程调度、优先级和关联性
|
7月前
|
API iOS开发
iOS 自定义转场动画 UIViewControllerTransitioning
iOS 自定义转场动画 UIViewControllerTransitioning
48 0