面试题

GCD和NSOperation

2018-03-25  本文已影响0人  开发者zhang

Cocoa 并发编程

iOS 中的多线程,是 Cocoa 框架下的多线程,通过 Cocoa 的封装,可以让我们更为方便的进行多线程编程。

在介绍 Cocoa 并发编程之前,我们先理清会提到的几个术语:


Cocoa 中封装了 NSThread, NSOperation, GCD 三种多线程编程方式,他们各有所长。

NSThread 是一个控制线程执行的对象,通过它我们可以方便的得到一个线程并控制它。NSThread 的线程之间的并发控制,是需要我们自己来控制的,可以通过 NSCondition 实现。

它的缺点是需要自己维护线程的生命周期和线程的同步和互斥等,优点是轻量,灵活

NSOperation 是一个抽象类,它封装了线程的细节实现,不需要自己管理线程的生命周期和线程的同步和互斥等。只是需要关注自己的业务逻辑处理需要和 NSOperationQueue 一起使用。使用 NSOperation 时,你可以很方便的设置线程之间的依赖关系。这在略微复杂的业务需求中尤为重要。

GCD(Grand Central Dispatch) 是 Apple 开发的一个多核编程的解决方法。在 iOS4.0 开始之后才能使用。GCD 是一个可以替代 NSThread 的很高效和强大的技术。当实现简单的需求时,GCD 是一个不错的选择。

在现代 Objective-C 中,苹果已经不推荐使用 NSThread 来进行并发编程,而是推荐使用 GCD 和 NSOperation,具体的迁移文档参见 Migrating Away from Threads。

Grand Central Dispatch(GCD)

Grand Central Dispatch(GCD) 是苹果在 Mac OS X 10.6 以及 iOS 4.0 开始引入的一个高性能并发编程机制,底层实现的库名叫 libdispatch。

GCD 主要的功劳在于:

概念:

Dispatch Queue:Dispatch Queue 顾名思义,是一个用于维护任务的队列,它可以接受任务(即可以将一个任务加入某个队列)然后在适当的时候执行队列中的任务。

这些东西中最经常用到的是 Dispatch Queue。之前提到 Dispatch Queue 就是一个类似队列的数据结构,而且是 FIFO(First In, First Out)队列,因此任务开始执行的顺序,就是你把它们放到 queue 中的顺序。GCD 中的队列有下面三种:

(我们可以创建多个串行队列,这些队列中的任务是串行执行的,但是这些队列本身可以并发执行。例如有四个串行队列,有可能同时有四个任务在并行执行,分别来自这四个队列。)

(在 iOS 5 之后,我们可以创建自己的并发队列。系统已经提供了四个全局可用的并发队列,后面会讲到。)

获取队列

按照上面提到的三种队列,我们有对应的三种获取队列的方式:

dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL); // OS X 10.7 和 iOS 4.3 之前
queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_SERIAL); // 之后
  1. DISPATCH_QUEUE_PRIORITY_HIGH
  2. DISPATCH_QUEUE_PRIORITY_DEFAULT
  3. DISPATCH_QUEUE_PRIORITY_LOW
  4. DISPATCH_QUEUE_PRIORITY_BACKGROUND

优先级依次降低。优先级越高的队列中的任务会更早执行:

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

当然我们也可以创建自己的并行队列:

queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_CONCURRENT);

不过一般情况下我们使用系统提供的 Default 优先级的 queue 就足够了。

dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
[imageVIew setImage:image];
});

自己创建的队列与系统队列有什么不同?

事实上,我们自己创建的队列,最终会把任务分配到系统提供的主队列四个全局的并行队列上,这种操作叫做 Target queues

(具体来说,我们创建的串行队列的 target queue 就是系统的主队列,我们创建的并行队列的 target queue 默认是系统 default 优先级的全局并行队列。所有放在我们创建的队列中的任务,最终都会到 target queue 中完成真正的执行。)


通过我们自己创建的队列,以及 dispatch_set_target_queue 和 barrier 等操作,可以实现比较复杂的任务之间的同步。

通常情况下,对于串行队列,我们应该自己创建,对于并行队列,就直接使用系统提供的 Default 优先级的 queue。

创建的 Queue 需要释放吗?

在 iOS 6 系统把 dispatch queue 也纳入了 ARC 管理的范围,就不需要我们进行手动管理了。

iOS6 以上就需要使用 strong 或者 weak 来修饰,不然会报错:

@property (nonatomic, strong) dispatch_queue_t queue;

执行任务

执行任务


NSOperation 和 NSOperationQueue

(NSOperation 本身是可以单独使用的,不过单独使用的话并不能体现出 NSOperation 的强大之处,通常还是使用 NSOperationQueue 来执行 NSOperation。)

NSOperation 是一个抽象类,我们需要继承它并且实现我们的子类

在 NSOperationQueue 中运行

NSOperationQueue 是一个专门用于执行 NSOperation 的队列。

在 OS X 10.6 之后,把一个 NSOperation 放到 NSOperationQueue 中,queue 会忽略 isAsynchronous 变量,总是会把 operation 放到后台线程中执行。

这样不管 operation 是不是异步的,queue 的执行都是不会造成主线程的阻塞的。

使用 Queue 可以很方便地进行并发操作,并且帮我们完成大部分的监视 operation 是否完成的操作。

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

[queue addOperation:op]; // add 完 operation 就立即启动了
[queue waitUntilAllOperationsAreFinished]; // 阻塞当前线程,直到所有的 operation 全都完成
NSLog(@"Main Function");

(像这样,我们可以添加各个各样的 operation 到 queue 中,只要这些 operation 都正确地重载了 isExecuting 和 isFinished,就可以正确地被并发执行。)

Dependency

NSOperation 可以通过 addDependency 来依赖于其他的 operation 完成,如果有很多复杂的 operation,我们可以形成它们之间的依赖关系图,来实现复杂的同步操作:

[updateUIOperation addDependency: workerOperation];

Cancellation

NSOperation 有如下几种的运行状态:

除 Finished 状态外,其他状态均可转换为 Canceled 状态。

当 NSOperation 支持了 cancel 操作时,NSOperationQueue 可以使用 cancelAllOperatoins 来对所有的 operation 执行 cancel 操作。

不过 cancel 的效果还是取决于 NSOperation 中代码是怎么写的。(比如 对于数据库的某些操作线程来说,cancel 可能会意味着 你需要把数据恢复到最原始的状态。)

maxConcurrentOperationCount

默认的最大并发 operation 数量是由系统当前的运行情况决定的(来源),我们也可以强制指定一个固定的并发数量。

Queue 的优先级

NSOperationQueue 可以使用 queuePriority 属性设置优先级,具体的优先级有下面几种:

typedef enum : NSInteger {
NSOperationQueuePriorityVeryLow = -8,
NSOperationQueuePriorityLow = -4,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
} NSOperationQueuePriority;

在 Queue 中优先级较高的会先执行。


GCD 与 NSOperation 的对比

这是面试中经常会问到的一点,这两个都很常用,也都很强大。对比它们可以从下面几个角度来说:

上一篇 下一篇

猜你喜欢

热点阅读