GCD
GCD是最经常使用的管理并行代码和执行异步操做的Unix系统层的API。GCD构造和管理队列中的任务。
队列是按先进先出(FIFO)管理对象的数据结构。队列相似电影院的售票窗口,票的销售是谁先到谁先服务。在等待线前面的人先去买他们的门票,在其他的后抵达的人以前。队列在计算机科学中是类似的,由于第一个添加到队列的对象也是第一个从队列中删除的对象。
操做队列 NSOperationQueue
GCD是一个底层的C的API,它使开发人员可以并行地执行任务。操做队列,另外一方面,是高度抽象的队列模型,是创建在GCD之上的。这意味着你能够并行执行任务就像GCD同样,但以面向对象的方式。简而言之,队列操做让编程更加简单。
不一样于GCD,它们不按先进先出的顺序。下面是操做队列和调度队列的不一样点:
1.不遵循先进先出:在操做队列中,你能够设置一个操做的执行优先级,你能够添加操做之间的依赖关系,这意味着你能够定义一些操做完成后才会执行其余操做。这就是为何它们不遵循先进先出。
2.默认状况下,它们同时操做:然而你不能把它的类型改变成串行队列。经过使用操做之间的依赖关系,在操做队列还存在一个工做区来依次执行任务。
3.操做队列是类NSOperationQueue的实例,其任务封装在NSOperation的实例里。
NSOperation
是苹果提供的一套多线程解决方案,实际上NSOperation是基于GCD 更高一层的封装,可是比GCD 更简单易用、代码可读性更高。
须要配合NSOperationQueue 来实现多线程。 默认状况下,单独使用NSOperation时系统执行同步操做,并无开辟新线程的能力,只有和配合NSOperationQueue 才能实现异步执行。
由于NSOperation是基于GCD的,那么使用起来也和GCD差很少,其中,NSOperation至关于GCD中的任务,而NSOperationQueue则至关于GCD中的队列。NSOperation实现多线程的使用步骤分为三步:
- 建立任务:先将须要执行的操做封装到一个NSOperation对象中。
- 建立队列:建立NSOperationQueue对象。
- 将任务加入到队列中:而后将NSOperation对象添加到NSOperationQueue中。
以后呢,系统就会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操做。
NSOperation是一个抽象类,它不能直接使用,因此你必须使用NSOperation子类。在iOS SDK里,咱们提供两个NSOperation的具体子类。这些类能够直接使用,但你也能够继承NSOperation来建立本身的类来执行操做。咱们能够直接使用的两个类:
1.NSBlockOperation——使用这个类来用一个或多个block初始化操做。操做自己能够包含多个块。当全部block被执行操做将被视为完成。
2.NSInvocationOperation——使用这个类来初始化一个操做,它包括指定对象的调用selector。
或者定义继承自NSOperation的子类,经过实现内部相应的方法来封装任务。
NSOperationQueue
和GCD中的并发队列、串行队列不一样的是,NSOperationQueue 一共有两种队列:主队列、其余队列。
其余队列中包含了串行、并发功能。
NSOperationQueue *queue = [NSOperationQueue mainQueue];//主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];//其余队列
将任务加入到队列中
1.先建立任务,再将建立好的任务加入到建立好的队列中去
- (void)addOperation:(NSOperation *)op;
2.无需建立任务,在block中执行任务,直接将任务block加入到队列中
(void)addOperationWithBlock:(void (^)(void))block;
控制串行执行和并发执行的关键
最大并发数:maxConcurrentOperationCount
默认状况下是-1,表示不进行限制,默认为并发执行。
当为1时,进行串行执行。
当>1时,进行并发执行。
操做依赖
NSOperation和NSOperationQueue最吸引人的地方是它能添加操做之间的依赖关系。好比说有A、B两个操做,其中A执行完操做,B才能执行操做,那么就须要让B依赖于A。
一些其余的方法:
- - (void)cancel; NSOperation提供的方法,可取消单个操做
- - (void)cancelAllOperations; NSOperationQueue提供的方法,能够取消队列的全部操做
- - (void)setSuspended:(BOOL)b; 可设置任务的暂停和恢复,YES表明暂停队列,NO表明恢复队列
- - (BOOL)isSuspended; 判断暂停状态
注意:
这里的暂停和取消并不表明能够将当前的操做当即取消,而是当当前的操做执行完毕以后再也不执行新的操做
- 暂停和取消的区别就在于:暂停操做以后还能够恢复操做,继续向下执行;而取消操做以后,全部的操做就清空了,没法再接着执行剩下的操做。
自定义子类:
使用main方法,不须要管理一些状态属性(如isExecuting 和 isFinished ),当main 方法返回的时候,这个operation 就结束了。 这种方式使用起来很是简单,main方法执行完就认为operation 结束了。因此通常能够用来执行同步任务。
若是你但愿拥有更多的控制权,或者想在一个操做中能够执行异步任务,那么就重写start 方法。
在这种状况下,必须手动管理操做的状态,只有当发送isFinished 的kvo 消息时,才认为是operation 结束。
因此NSOperation的优点是什么?
1.首先,它们经过NSOperation类里的方法addDependency(op:NSOperation)支持依赖。当你须要开始一个依赖于其它操做执行的操做,你会须要NSOperation。
2.其次,你能够经过下面这些值设置属性queuePriority来改变执行优先级:
3.对于任何给定的队列,你能够取消一个特定的或全部的操做。操做能够在被添加到队列后被取消。取消是经过调用NSOperation类里的方法cancel()。当你取消任何操做的时候,咱们有三个场景,其中一个会发生:
你的操做已经完成。在这种状况下,取消方法没有效果。
你的操做已经被执行。在这种状况下,系统不会强制操做代码中止,而是属性cancelled被置为true。
你的操做仍在队列中等待。在这种状况下,你的操做将不会被执行。
4.NSOperation有3个有用的布尔属性,finished、 cancelled和ready。一旦操做执行完成,finisher将被置为true。一旦操做被取消,cancelled将被置为true。一旦准备即将被执行,ready将被置为true。
5.任何NSOperation有一个选项来设置回调,一旦任务完成将会被调用。在NSOperation里,一旦属性finished被置为true,这个block将被调用。
原理探析:
demo:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configurationQueue];
LDNSOperation *operation = [[LDNSOperation alloc] init];
[self.operationQueue addOperation:operation];
[NSThread sleepForTimeInterval:3];
[operation cancel];
}
-(void)configurationQueue{
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 4;
}
LDNSOperation为NSOperation的子类,重写start方法:
-(void)start{
while (true) {
if(self.cancelled){
NSLog(@"已经取消");
return;
}
NSLog(@"start");
[NSThread sleepForTimeInterval:1];
}
}
1.
[self.operationQueue addOperation:operation];
添加一个未完成的NSOperation ,其实就是将NSOperation 添加到一个动态的数组中。
internal 就是一个内部类,指的是NSOperationQueue 。
使用了KVO手动通知,进行operations 和 operationCount 的改变通知。
完成的话,就从operations 中删除。
若是准备完成,就添加到waiting 中,等待被执行。
_execute 方法:
若是没有暂停,就从waiting 中取出第一个,而且删除waiting 中的这个数据。
添加isFinished 属性观察
若是是并发的话,就立刻执行。
若是不是,使用detachNewThreadSelector来建立新的线程去执行start。
全部的线程都很忙,而且没有达到threadCount最大值时,会建立新的线程。
_execute 是一个执行队列,依次将等待队列里全部的operation 进行start 。
对于start 函数来说,一个NSOperation 并无新建一个线程。 依然操做在[NSTread currentThread]中。
如今来思考下,也就明白了为何NSOperationQueue要有两种处理方式了,若是NSOperation支持并发,而后NSOperationQueue在为其分配线程,那就是线程里面又跑了一条线程,这样就很尴尬了,经过isConcurrent能够避免这种现象。