NSOperation
、NSOperationQueue
是苹果提供的一套多线程解决方案,是基于GCD
更高一层的封装,彻底面向对象。但比 GCD
更简单易用、代码可读性更高。数组
- 好处:
可添加完成的代码块complete
,在操做完成后执行。 添加操做之间的依赖关系,方便控制执行顺序。 设定操做执行的优先级。 能够取消一个操做的执行。 使用 KVO 观察执行状态:isExecuteing、isFinished、isCancelled
。安全
在 NSOperation、NSOperationQueue
中也有相似的“任务”和“队列”的概念。markdown
任务,或操做。即,你在线程中执行的那段代码。NSOperation
是一个抽象类,要使用其子类 NSInvocationOperation、NSBlockOperation
,或者自定义子类来封装任务。多线程
指用来存放操做的队列。并发
主队列、自定义队列。 主队列运行在主线程之上,而自定义队列在后台执行。异步
不一样于 GCD 中的队列 FIFO(先进先出)的原则。对于添加到NSOperationQueue
中的任务,首先进入准备就绪的状态(就绪状态取决于任务之间的依赖关系),而后进入就绪状态的任务的开始执行顺序(而非执行完成的顺序)由任务之间相对的优先级决定。性能
任务队列经过设置最大并发操做数maxConcurrentOperationCount
来控制并发、串行。atom
默认状况下,NSOperation
单独使用时,系统同步执行操做。只有配合 NSOperationQueue
才能更好地实现异步执行。spa
建立任务:先将须要执行的任务,封装到一个 NSOperation
对象中。 建立队列:即NSOperationQueue
对象。 将任务加入到队列中:将NSOperation
对象添加到 NSOperationQueue
对象中。线程
系统就会自动将NSOperationQueue
中的 NSOperation
取出来,在新线程中执行操做。
NSOperation
是个抽象类,不能直接用来封装任务。要使用它的子类来封装任务:NSInvocationOperation、NSBlockOperation
以及自定义的子类。
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
[op start]; // 开始执行
复制代码
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// do some thing...
}];
[op start];
复制代码
此外,能够为任务对象添加额外任务。
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// do something...
}];
// 添加额外的操做
[op addExecutionBlock:^{
// do otherthing...
}];
复制代码
- 在没有使用
NSOperationQueue
、在主线程中单独使用NSOperation
的子类对象 执行一个任务的状况下,任务是在当前线程执行的,并无开启新线程。- 使用子类
NSBlockOperation
,并调用方法addExecutionBlock:
的状况下,若是添加的操做的个数多,就会自动开启新线程。
有两种:主队列、自定义队列。
NSOperationQueue *queue = [NSOperationQueue mainQueue];
复制代码
凡是添加到主队列中的操做,都会放到主线程中执行。
添加到这种队列中的操做,就会自动放到子线程中执行;同时包含串行、并发功能。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue. maxConcurrentOperationCount = 3;
复制代码
可设置属性maxConcurrentOperationCount
最大并发操做数,决定串行、并发。
属性
maxConcurrentOperationCount
= 1; // 串行队列,任务只能按顺序串行执行的 = 2; // 并发队列,>2时,任务是并发执行的,可同时执行多个操做 = 8; // 并发队列 固然这个值不该超过系统限制,即便设置了一个很大的值,系统也会自动调整为 min。
方法addOperation:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
//.....
}];
[op3 addExecutionBlock:^{
//.....
}];
[queue addOperation:op1];
[queue addOperation:op3];
复制代码
方法addOperationWithBlock:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 添加操做到队列中
[queue addOperationWithBlock:^{
//.....
}];
复制代码
操做和操做队列、使用步骤和基本使用方法、控制串行/并发执行、
经过依赖,能够很方便的控制任务之间的执行前后顺序。
addDependency:(NSOperation *)op;
添加依赖
removeDependency:(NSOperation *)op;
移除依赖 NSArray<NSOperation *> *dependencies;
在当前任务开始执行以前,已完成任务对象的数组
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
//.....
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
//.....
}];
// 添加依赖
[op2 addDependency:op1]; //让op2 依赖于 op1。表示先执行op1
[queue addOperation:op1];
[queue addOperation:op2];
复制代码
当一个任务的全部依赖都已经完成时,任务对象一般会进入准备就绪状态,等待执行。
###NSOperation 优先级 NSOperation
提供了优先级属性queuePriority
。默认状况下,全部新建立的操做对象优先级都是NSOperationQueuePriorityNormal
。但能够经过setQueuePriority:
方法来改变在同一队列中的任务的优先级。
queuePriority
属性适用于同一队列中的任务,不一样队列的任务不适用。
优先级的取值: NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
(默认) NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
queuePriority
决定了进入准备就绪状态下的任务之间的开始执行顺序
。而且,优先级不能取代依赖关系。若是,一个队列中既包含了准备就绪状态的任务A,又包含了未准备就绪的任务B,B的优先级比A高。那么,会优先执行B。若是要控制操做间的启动顺序,则必须使用依赖关系。
处理完子行程的任务,返回主行程处理
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
// .....
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 回到主线程, 进行一些 UI 刷新等操做
}];
}];
复制代码
正在执行的操做,没法取消。只能取消未执行的操做。
1.可经过 cancel 方法,取消未执行的单个操做。
NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block11");
}];
NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block22");
}];
NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block33");
}];
[block3 cancel];
[queue1 addOperations:@[block1,block2,block3] waitUntilFinished:YES];
复制代码
// 移除队列里面全部的操做
[queue1 cancelAllOperations];
复制代码
3.挂起队列,使队列任务再也不执行,但正在执行的操做没法挂起。
queue1.suspended = YES;
复制代码
要实现取消正在执行的操做,能够自定义NSOperation,其实就是拦截main方法。
main方法: 1.任何操做在执行时,首先会调用start方法,它会更新操做的状态过滤操做(如过滤掉处于“取消”状态的操做) 2.经start方法过滤后,只有正常可执行的操做,就会调用main方法。 3.重写操做的入口方法(main),就能够在这个方法里面指定操做执行的任务。 4.main方法默认是在子线程异步执行的。
有多个线程可能会同时运行一段代码。若是每次运行结果和单线程运行的结果同样,且其余变量的值也和预期的是同样的,就是线程安全的。 若每一个线程中对全局变量、静态变量只有读操做(无写操做),通常这个变量是线程安全的。 如有多个线程同时执行写操做(更改变量的值),就须要考虑线程同步,不然就可能影响线程安全。
可理解为线程 A 和 线程 B 一块配合,A 执行到必定程度时要依靠线程 B 的某个结果,因而停下来,示意 B 运行;B 依言执行,再将结果给 A;A 再继续操做。 例子:火车票售卖
能够给线程加锁,在一个线程执行该操做的时候,不容许其余线程进行操做。
iOS 实现线程加锁有不少种方式:
dispatch_semaphore
:建议使用,性能较好 NSLock
@synchronized
:性能最差 os_unfair_lock
:iOS10开始 NSCondition
NSConditionLock
pthread_mute
OSSpinLock
:iOS10 废弃 atomic(property) set/get
:原子操做
思想:执行写操做前,上锁,写操做完,解锁。
自旋锁:
会忙等。所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释放锁。 适用的状况 预计线程等待锁的时间很短 加锁的代码(临界区)常常被调用,但竞争状况不多发生 CPU资源不紧张、多核处理器
互斥锁:
会休眠。 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时cpu能够调度其余线程工做。直到被锁资源释放锁。此时会唤醒休眠线程。 适用的状况 预计线程等待锁的时间较长 临界区竞争很是激烈 临界区有IO操做 临界区代码复杂或者循环量大 单核处理器
死锁:
一般是指2个操做相互等待对方完成,形成死循环,因而2个操做都没法进行,就产生了死锁。
.
建议在复杂项目中 使用。
建议在简单项目中 使用。
1.NSOperationQueue能够轻松在Operation间设置依赖关系,而GCD 须要写不少代码才能实现。 2. NSOperation能够很方便地调整执⾏顺序、设置最⼤并发数。 3.NSOperationQueue⽀支持KVO,能够监测operation的状态:是否正在执行 (isExecuted)
、 是否结束(isFinished)
,是否取消(isCanceld)
4.GCD只⽀支持FIFO的队列列