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月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
150 0
|
6月前
|
Java 数据库连接 调度
面试题:用过线程池吗?如何自定义线程池?线程池的参数?
字节跳动面试题:用过线程池吗?如何自定义线程池?线程池的参数?
88 0
|
6月前
|
iOS开发 UED
实现一个自定义的iOS动画效果
【4月更文挑战第9天】本文将详细介绍如何在iOS平台上实现一个自定义的动画效果。我们将通过使用Core Animation框架来实现这个动画效果,并展示如何在不同的场景中使用它。文章的目标是帮助读者理解如何使用Core Animation框架来创建自定义动画,并提供一个简单的示例代码。
52 1
|
15天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
31 1
|
24天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
46 5
|
6月前
|
消息中间件 安全 前端开发
小米面试:如何实现优先级线程池?
我们知道,线程池中的所有线程都是由统一的线程工厂来创建的,当我们指定线程工厂时,线程池中的所有线程会使用我们指定的线程工厂来创建线程;但如果没有指定线程工厂,则会使用默认的线程工厂 DefaultThreadFactory 来创建线程,核心源码如下: ```java DefaultThreadFactory() { @SuppressWarnings("removal") SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() :
74 1
小米面试:如何实现优先级线程池?
|
1月前
|
Swift iOS开发 UED
实现一个自定义的iOS动画效果
本文介绍如何使用Swift和UIKit在iOS应用中实现一个自定义按钮动画,当按钮被点击时,其颜色从蓝色渐变为绿色,形状从圆形变为椭圆形,释放后恢复原状。通过UIView动画方法实现这一效果,代码示例展示了动画的平滑过渡和状态切换,有助于提升应用的视觉体验和用户交互。
49 1
|
1月前
|
Java Linux 调度
Java线程的优先级详解
Java线程的优先级机制允许开发者根据程序需求为线程设定不同优先级,范围通常在1到10之间,默认优先级为5。高优先级线程在执行时通常会得到更多的CPU时间,但这并不意味着低优先级线程会被完全忽略。系统资源分配仍然取决于具体的调度策略。理解线程优先级有助于优化多线程应用的性能。
|
2月前
|
Swift iOS开发 UED
揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【9月更文挑战第5天】本文通过具体案例介绍如何在iOS应用中使用Swift与UIKit实现自定义按钮动画,当用户点击按钮时,按钮将从圆形变为椭圆形并从蓝色渐变到绿色,释放后恢复原状。文中详细展示了代码实现过程及动画平滑过渡的技巧,帮助读者提升应用的视觉体验与特色。
61 11
|
3月前
|
存储 Java 开发者
HashMap线程安全问题大揭秘:ConcurrentHashMap、自定义同步,一文让你彻底解锁!
【8月更文挑战第24天】HashMap是Java集合框架中不可或缺的一部分,以其高效的键值对存储和快速访问能力广受开发者欢迎。本文深入探讨了HashMap在JDK 1.8后的底层结构——数组+链表+红黑树混合模式,这种设计既利用了数组的快速定位优势,又通过链表和红黑树有效解决了哈希冲突问题。数组作为基石,每个元素包含一个Node节点,通过next指针形成链表;当链表长度过长时,采用红黑树进行优化,显著提升性能。此外,还介绍了HashMap的扩容机制,确保即使在数据量增大时也能保持高效运作。通过示例代码展示如何使用HashMap进行基本操作,帮助理解其实现原理及应用场景。
53 1