iOS多线程-NSOperation

简介

NSOperationNSOperationQueue 是苹果提供的一套多线程解决方案,是基于GCD更高一层的封装,彻底面向对象。但比 GCD 更简单易用、代码可读性更高。数组

  • 好处:

可添加完成的代码块complete,在操做完成后执行。 添加操做之间的依赖关系,方便控制执行顺序。 设定操做执行的优先级。 能够取消一个操做的执行。 使用 KVO 观察执行状态:isExecuteing、isFinished、isCancelled安全

NSOperation、NSOperationQueue 中也有相似的“任务”和“队列”的概念。markdown

1.NSOperation

任务,或操做。即,你在线程中执行的那段代码。NSOperation是一个抽象类,要使用其子类 NSInvocationOperation、NSBlockOperation,或者自定义子类来封装任务。多线程

2.NSOperationQueue

指用来存放操做的队列。并发

  • 队列类型

主队列、自定义队列。 主队列运行在主线程之上,而自定义队列在后台执行。异步

  • 任务状态和顺序

不一样于 GCD 中的队列 FIFO(先进先出)的原则。对于添加到NSOperationQueue 中的任务,首先进入准备就绪的状态(就绪状态取决于任务之间的依赖关系),而后进入就绪状态的任务的开始执行顺序(而非执行完成的顺序)由任务之间相对的优先级决定。性能

  • 并发和串行

任务队列经过设置最大并发操做数maxConcurrentOperationCount来控制并发、串行。atom

3. NSOperation实现多线程

默认状况下,NSOperation 单独使用时,系统同步执行操做。只有配合 NSOperationQueue 才能更好地实现异步执行。spa

  • 使用步骤分为3步

建立任务:先将须要执行的任务,封装到一个 NSOperation 对象中。 建立队列:即NSOperationQueue 对象。 将任务加入到队列中:将NSOperation对象添加到 NSOperationQueue 对象中。线程

系统就会自动将NSOperationQueue中的 NSOperation 取出来,在新线程中执行操做。

3.1建立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: 的状况下,若是添加的操做的个数多,就会自动开启新线程。
2.建立NSOperationQueue队列

有两种:主队列、自定义队列。

  • 主队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
复制代码

凡是添加到主队列中的操做,都会放到主线程中执行。

  • 自定义队列

添加到这种队列中的操做,就会自动放到子线程中执行;同时包含串行、并发功能。

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

queue. maxConcurrentOperationCount = 3;
复制代码

可设置属性maxConcurrentOperationCount最大并发操做数,决定串行、并发。

属性maxConcurrentOperationCount

= 1; // 串行队列,任务只能按顺序串行执行的 = 2; // 并发队列,>2时,任务是并发执行的,可同时执行多个操做 = 8; // 并发队列 固然这个值不该超过系统限制,即便设置了一个很大的值,系统也会自动调整为 min。

3.将操做加入到队列中

方法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:^{
        //.....
    }];
复制代码

操做和操做队列、使用步骤和基本使用方法、控制串行/并发执行、

  • 把任务添加到队列中,至关于调用了任务的start方法

NSOperation 依赖

经过依赖,能够很方便的控制任务之间的执行前后顺序。

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];
复制代码
  1. cancelAllOperations方法,但没法移除 正在执行的操做。
// 移除队列里面全部的操做
[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个操做都没法进行,就产生了死锁。

经常使用属性和方法概括

.

NSOperation 和GCD的对比

  • NSOperation 是对线程的高度抽象,在项目中使 用它,会使项目的程序结构更好。子类化 NSOperation 的设计思路,是具备面向对 象的优势(复用、封装),使得实现是多线程支持,而接口简单。

建议在复杂项目中 使用。

  • GCD 自己很是简单、易用,对于不复杂的多线程操 做,会节省代码量,而 Block 参数的使用,会是代码更为易读。

建议在简单项目中 使用。

  • NSOperation的优势

1.NSOperationQueue能够轻松在Operation间设置依赖关系,而GCD 须要写不少代码才能实现。 2. NSOperation能够很方便地调整执⾏顺序、设置最⼤并发数。 3.NSOperationQueue⽀支持KVO,能够监测operation的状态:是否正在执行 (isExecuted)、 是否结束(isFinished),是否取消(isCanceld) 4.GCD只⽀支持FIFO的队列列

相关文章
相关标签/搜索