NSOperation 与 Grand Central Dispatch
Posted
技术标签:
【中文标题】NSOperation 与 Grand Central Dispatch【英文标题】:NSOperation vs Grand Central Dispatch 【发布时间】:2012-05-09 12:59:56 【问题描述】:我正在学习 ios 的并发编程。到目前为止,我已经阅读了有关 NSOperation
/NSOperationQueue
和 GCD
的信息。 使用NSOperationQueue
而不是GCD
的原因是什么?反之亦然?
听起来GCD
和NSOperationQueue
都从用户那里抽象出NSThreads
的显式创建。然而,这两种方法之间的关系对我来说并不清楚,所以任何反馈都值得赞赏!
【问题讨论】:
+1 表示好问题 - 对结果感到好奇。到目前为止,我刚刚读到 GCD 可以很容易地跨 CPU 内核分派,使其成为“新热点”。 一些相关的讨论可以在这个问题中找到:Why should I choose GCD over NSOperation and blocks for high-level applications? cocoacasts.com/… 【参考方案1】:我同意@Sangram 和其他答案,但想补充几点。如果我错了,请纠正我。
我认为现在@Sangram 回答的前两点无效(i. 操作控制 ii. 依赖项)。我们也可以通过使用 GCD 来实现这两个。尽量用代码来解释(不要关注代码质量,仅供参考)
func methodsOfGCD()
let concurrentQueue = DispatchQueue.init(label: "MyQueue", qos: .background, attributes: .concurrent)
//We can suspend and resume Like this
concurrentQueue.suspend()
concurrentQueue.resume()
//We can cancel using DispatchWorkItem
let workItem = DispatchWorkItem
print("Do something")
concurrentQueue.async(execute: workItem)
workItem.cancel()
//Cam add dependency like this.
//Operation 1
concurrentQueue.async(flags: .barrier)
print("Operation1")
//Operation 2
concurrentQueue.async(flags: .barrier)
print("Operation2")
//Operation 3.
//Operation 3 have dependency on Operation1 and Operation2. Once 1 and 2 will finish will execute Operation 3. Here operation queue work as a serial queue.
concurrentQueue.async(flags: .barrier)
print("Operation3")
【讨论】:
也许你可以解释一下let workItem = DispatchWorkItem
用 objc 完成后。【参考方案2】:
GCD
是一个基于 C 的低级 API,可以非常简单地使用基于任务的并发模型。 NSOperation
和 NSOperationQueue
是做类似事情的 Objective-C 类。 NSOperation
是最先引入的,但从10.5 和iOS 2 开始,NSOperationQueue
和朋友们在内部使用GCD
实现。
一般来说,您应该使用适合您需要的最高抽象级别。这意味着您通常应该使用NSOperationQueue
而不是GCD
,除非您需要执行NSOperationQueue
不支持的操作。
请注意,NSOperationQueue
不是 GCD 的“简化”版本;事实上,使用NSOperationQueue
可以非常简单地完成很多事情,而使用纯GCD
需要大量工作。 (例如:带宽受限的队列,一次只能运行 N 个操作;建立操作之间的依赖关系。NSOperation
非常简单,GCD
非常困难。)Apple 努力利用 GCD 创建一个非常好的对象友好的 API,带有 NSOperation
。除非您有理由不这样做,否则请充分利用他们的工作。
警告:
另一方面,如果你真的只需要发送一个块,并且不需要NSOperationQueue
提供的任何附加功能,那么使用 GCD 并没有什么问题。请确保它是适合这项工作的工具。
【讨论】:
NSOperation 是一个具体的抽象类。 @Sandy 其实正好相反,NSOperation 使用 GCD(至少在 iOS 和 OS X 的更高版本中)。 @BJ Homer 我们可以在串行调度队列中添加任务以实现 depeancy。所以证明操作队列比那个更有优势 @RajAggrawal 是的,这行得通……但是你被串行队列卡住了。 NSOperation 可以“在完成其他三个操作后执行此操作,但同时执行所有其他操作”。操作依赖关系甚至可以存在于不同队列上的操作之间。大多数人不需要它,但如果你这样做,NSOperation 会是更好的选择。【参考方案3】:GCD
是一个基于 C 的低级 API。NSOperation
和 NSOperationQueue
是 Objective-C 类。NSOperationQueue
是 GCD
的 Objective C 包装器。
如果您使用的是 NSOperation,那么您就是在隐式使用 Grand Central Dispatch。
GCD 优于 NSOperation:i.实施
对于GCD
的实现非常轻量级NSOperationQueue
复杂且重量级
NSOperation 相对于 GCD 的优势:
我。控制操作
您可以暂停、取消、恢复NSOperation
ii.依赖关系
您可以在两个 NSOperations
之间建立依赖关系
直到它的所有依赖项都为完成返回 true,操作才会开始。
iii.运行状态 可以监视操作或操作队列的状态。 准备、执行或完成
iv.最大操作次数 您可以指定可以同时运行的最大排队操作数
何时选择GCD
或NSOperation
当您想要更多地控制队列(以上都提到)时,请使用NSOperation
对于需要更少开销的简单情况
(您只想做一些“进入后台”的工作,只需很少的额外工作)使用GCD
参考:https://cocoacasts.com/choosing-between-nsoperation-and-grand-central-dispatch/ http://iosinfopot.blogspot.in/2015/08/nsthread-vs-gcd-vs-nsoperationqueue.html http://nshipster.com/nsoperation/
【讨论】:
如前所述,最大操作数可以在 NSOperationQueue 中指定,那么 GCD 中的最大操作数(调度队列)可以是多少?假设我有一个项目,那么我可以做多少个操作(调度队列)。或者它们是我们可以做到的任何最大限制。 这取决于系统条件这里是详细信息:***.com/questions/14995801/… 我们也可以使用 DispatchWorkItem 取消 GCD 中的任务,我们也可以暂停和恢复 @Ankitgarg 在 DispatchWorkItem 上调用 cancel 将停止执行尚未运行的任务,但不会停止已经执行的任务。以及如何暂停/恢复 DispatchWorkItem??【参考方案4】:NSQueueOperations 和 GCD 都允许通过释放 UI 应用程序主线程在后台在单独的线程上执行繁重的计算任务。
好吧,根据之前的帖子,我们看到 NSOperations 具有 addDependency 以便您可以按顺序将您的操作一个接一个地排队。
但我还阅读了有关 GCD 串行队列的信息,您可以使用 dispatch_queue_create 在队列中创建运行您的操作。这将允许以顺序方式一个接一个地运行一组操作。
NSQueueOperation 相对于 GCD 的优势:
它允许添加依赖项并允许您删除依赖项,因此对于一个事务,您可以使用依赖项顺序运行,而其他事务在 GCD 时同时运行 不允许这样运行。
如果一个操作在队列中,很容易取消它,如果它正在运行则可以停止。
您可以定义最大并发操作数。
您可以暂停他们在队列中的操作
您可以查看队列中有多少待处理的操作。
【讨论】:
【参考方案5】:另一个比 GCD 更喜欢 NSOperation 的原因是 NSOperation 的取消机制。例如,一个像 500px 的 App 展示了几十张照片,使用 NSOperation 我们可以在滚动表格视图或集合视图时取消对不可见图像单元格的请求,这可以大大提高 App 性能并减少内存占用。 GCD 不能轻易支持这一点。
还可以通过 NSOperation 实现 KVO。
Here 是 Eschaton 的一篇文章,值得一读。
【讨论】:
值得注意的是,如果你取消的是加载图片的网络操作,那么你不需要NSOperation
,因为NSURLSessionTask.cancel
和NSURLSession.invalidateAndCancel
提供了这个功能。一般来说,NSURLSession
提供了NSOperationQueue
的一些功能,因为NSURLSessionTask
提供了NSOperation
的一些功能
@algal 正如这里所解释的 (***.com/questions/21918722/…),似乎 NSURLSession 使用 NSOperationQueue 作为构建块。【参考方案6】:
嗯,NSOperations 只是一个建立在 Grand Central Dispatch 之上的 API。因此,当您使用 NSOperations 时,您实际上仍在使用 Grand Central Dispatch。 只是 NSOperations 为您提供了一些您可能喜欢的花哨功能。您可以使某些操作依赖于其他操作,在汇总项目后重新排序队列,以及其他类似的事情。 事实上,ImageGrabber 已经在使用 NSOperations 和操作队列了! ASIHTTPRequest 在后台使用它们,如果您愿意,您可以配置它用于不同行为的操作队列。 那么你应该使用哪个?无论哪个对您的应用有意义。对于这个应用程序来说它非常简单,所以我们直接使用 Grand Central Dispatch,不需要 NSOperation 的花哨功能。但如果您的应用需要它们,请随意使用!
【讨论】:
【参考方案7】:GCD 非常易于使用 - 如果您想在后台执行某项操作,您所需要做的就是编写代码并将其分派到后台队列中。用 NSOperation 做同样的事情需要做很多额外的工作。
NSOperation 的优势在于 (a) 您有一个可以向其发送消息的真实对象,以及 (b) 您可以取消 NSOperation。这不是微不足道的。您需要继承 NSOperation,您必须正确编写代码,以便取消和正确完成任务都能正常工作。所以对于简单的事情你使用 GCD,对于更复杂的事情你创建一个 NSOperation 的子类。 (有 NSInvocationOperation 和 NSBlockOperation 的子类,但它们所做的一切都使用 GCD 更容易完成,因此没有充分的理由使用它们)。
【讨论】:
【参考方案8】:GCD 确实比 NSOperationQueue 低级,它的主要优点是它的实现非常轻量级,并且专注于无锁算法和性能。
NSOperationQueue 确实提供了 GCD 中不可用的功能,但它们的成本不菲.
如果您需要 NSOperationQueue 提供的设施,请使用它,但如果 GCD 足以满足您的需求,我建议您直接使用它以获得更好的性能、显着降低 CPU 和电源成本以及更大的灵活性。
【讨论】:
【参考方案9】:根据my answer to a related question,我不同意 BJ 的观点,并建议您首先查看 GCD 而不是 NSOperation / NSOperationQueue,除非后者提供了 GCD 不需要的东西。
在 GCD 之前,我在我的应用程序中使用了很多 NSOperations / NSOperationQueues 来管理并发。然而,自从我开始定期使用 GCD 以来,我几乎完全用块和调度队列替换了 NSOperations 和 NSOperationQueues。这源于我在实践中使用这两种技术的方式,以及我对它们执行的分析。
首先,在使用 NSOperations 和 NSOperationQueues 时会产生不小的开销。这些是 Cocoa 对象,它们需要被分配和释放。在我编写的一个以 60 FPS 渲染 3-D 场景的 iOS 应用程序中,我使用 NSOperations 来封装每个渲染帧。当我对此进行分析时,这些 NSOperations 的创建和拆除占运行应用程序中 CPU 周期的很大一部分,并且正在减慢速度。我用简单的块和 GCD 串行队列替换了这些,这种开销消失了,从而显着提高了渲染性能。这不是我注意到使用 NSOperations 开销的唯一地方,我在 Mac 和 iOS 上都看到过。
其次,在使用 NSOperations 时很难匹配基于块的调度代码的优雅。将几行代码包装在一个块中并调度它以在串行或并发队列上执行非常方便,而创建自定义 NSOperation 或 NSInvocationOperation 来执行此操作需要更多支持代码。我知道你可以使用 NSBlockOperation,但你也可能会向 GCD 发送一些东西。在我看来,将这些代码封装在与应用程序中的相关处理内联的块中会比使用单独的方法或封装这些任务的自定义 NSOperation 更好地组织代码。
NSOperations 和 NSOperationQueues 仍然有很好的用途。 GCD 没有真正的依赖概念,其中 NSOperationQueues 可以设置非常复杂的依赖图。在少数情况下,我会为此使用 NSOperationQueues。
总的来说,虽然我通常主张使用完成任务的最高抽象级别,但这是我主张使用 GCD 的较低级别 API 的一种情况。在我与之交谈过的 iOS 和 Mac 开发人员中,绝大多数人选择使用 GCD 而不是 NSOperations,除非他们的目标是不支持它的操作系统版本(iOS 4.0 和 Snow Leopard 之前的那些)。
【讨论】:
我只是有点不同意;我经常使用普通的 GCD。但我认为你在这个答案中过于低估了 NSBlockOperation 。 NSOperationQueue 的所有优点(依赖项、可调试性等)也适用于块操作。 @BJHomer - 我认为避免使用 NSBlockOperation 在我的情况下更多是个人喜好问题,尽管在看到使用 NSOperation 的开销拖累了几个应用程序之后,我一般都避开了 NSOperation .如果我要使用块,我倾向于全押在 GCD 上,除非我需要依赖支持。 +1,感谢您的分析。 Apple 似乎同时提倡两者(如 WWDC 2012 的并发 UI 会议),因此非常感谢。 @VolureDarkAngel - GCD 在处理这样的调度方面非常快。在您描述的情况下,它不应该成为您的瓶颈,除非您由于 I/O 访问速度慢或类似的原因以某种方式将一堆更新备份到队列中。不过,这里可能不是这种情况。 @asma22 - 可以分块进行计算是很常见的,但一个阶段的最终计算可能需要前几个阶段的结果。在这种情况下,您可以使后面的操作依赖于前面的操作,并且调度将被管理,以便在最后一个运行之前完成所有操作。以上是关于NSOperation 与 Grand Central Dispatch的主要内容,如果未能解决你的问题,请参考以下文章
iOS多线程编程(四)------ GCD(Grand Central Dispatch)
iOS多线程开发系列之Grand Central Dispatch(GCD)
dispatch_async 与 NSOperation 队列