【问题标题】:iphone - Should I use NSOperationQueue and NSOperation instead of NSThread?iphone - 我应该使用 NSOperationQueue 和 NSOperation 而不是 NSThread 吗?
【发布时间】:2012-09-05 14:30:07
【问题描述】:

我的应用面临设计问题。


基本上,以下是我将在我的应用程序中执行的操作。

单个任务是这样的:

  1. 从底层 CoreData 数据库中读取自定义对象
  2. 从 url 下载 json
  3. 解析 json 以更新自定义对象或创建新对象(解析可能需要 1 - 3 秒,大数据)
  4. 分析自定义对象(会涉及一些计算,可能需要 1 - 5 秒)
  5. 将自定义对象保存到 CoreData 数据库中。

可能有多个任务同时执行。

一个任务中的步骤显然是ordered(即没有步骤2下载json,步骤3不能继续),但它们也可以是discrete。我的意思是,例如,task2的第4步可以在task1的第3步之前执行(如果task2的下载速度比task1的快)

任务有优先级。用户可以启动具有更高优先级的任务,因此该任务的所有步骤都将尝试在所有其他步骤之前执行。


我希望 UI 可以尽可能地响应。

所以我打算创建一个优先级最低的 NSThread。

我在该线程中放置了一个自定义优先级事件队列。任务的每一步都成为一个事件(工作单元)。因此,例如,第 1 步下载 json 成为一个事件。下载后,该事件为第 3 步生成另一个事件并放入队列中。每个事件都有自己的优先级设置。


现在我看到这篇文章:Concurrency and Application Design。苹果建议我们Move Away from Threads 并使用GCDNSOperation

我发现NSOperation 非常符合我的草稿设计。但我有以下问题:

  • 考虑到 iPhone/iPad cpu cores,我应该只使用一个 NSOperationQueue 还是创建多个?
  • NSOperationQueue 或 NSOperation 会以最低线程优先级执行吗?执行是否会影响 UI 响应(我很在意,因为这些步骤涉及计算)?
  • 我可以从另一个生成一个 NSOperation 并将其放入队列吗?我在 NSOperation 中没有看到队列属性,我怎么知道队列?
  • 如何配合 NSOperationQueue 和 CoreData?每次访问 CoreData 时,我应该创建一个新的上下文吗?会很贵吗?
  • 任务的每一步都变成了 NSOperation,这样的设计是否正确?

谢谢

【问题讨论】:

  • 这是很多问题。考虑将每个问题作为一个单独的问题提出。只有最后一个需要有关您的特定应用程序的任何信息。

标签: iphone objective-c nsthread nsoperation nsoperationqueue


【解决方案1】:

考虑到 iPhone/iPad cpu cores,我应该只使用一个 NSOperationQueue 还是创建多个?

两个(CPU、网络+I/O)或三个(CPU、网络、I/O)串行队列应该适用于大多数情况,以保持应用响应和您的程序流式处理工作受他们的约束。当然,您可能会发现另一种组合/公式适用于您的特定工作分配。

NSOperationQueue 或 NSOperation 会以最低的线程优先级执行吗?执行是否会影响 UI 响应(我很在意,因为这些步骤涉及计算)?

默认情况下不是。如果您想降低优先级,请参阅-[NSOperation setThreadPriority:]

我可以从另一个生成一个 NSOperation 并将其放入队列吗?我在 NSOperation 中没有看到队列属性,我怎么知道队列?

当然。如果您使用我概述的串行方法,则很容易找到正确的队列——或者您可以使用 ivar。

如何将 NSOperationQueue 与 CoreData 配合使用?每次访问 CoreData 时,我应该创建一个新的上下文吗?会很贵吗?

(无评论)

任务的每一步都变成了一个NSOperation,这样的设计正确吗?

是的 - 将您的队列划分为它所绑定的资源是个好主意。

【讨论】:

    【解决方案2】:
    • 从外观上看,NSOperationQueue 就是您所追求的。您可以设置同时运行的并发操作数。如果使用多个 NSOperation,它们都会同时运行……除非你自己处理一个队列,这和使用 NSOperationQueue 是一样的

    • 线程优先级...我不太清楚你的意思,但在 iOS 中,UI 绘制、事件和用户交互都在主线程上运行。如果您在后台线程上运行,无论您运行的操作多么复杂或占用大量 CPU 资源,界面仍然会响应

    • 您应该在主线程上生成和处理操作,因为它不会花费任何时间,您只需在后台线程中运行它们,这样您的主线程就不会被锁定

    • CoreData,我没有专门使用它,但到目前为止,我使用的每个 Core~ 在后台线程上都可以完美运行,所以应该不是问题

      李>
    • 就设计而言,这只是一种观点......至于我,我会为每个任务设置一个 NSOperation,并让它处理所有步骤。如果您想提供一些反馈或继续其他下载或其他内容,可能会在步骤完成时编写回调

    【讨论】:

    • 感谢您的回答。将一步作为 NSOperation 的原因是一项任务可能需要太多时间。如果用户想要优先处理某项任务,同时正在执行另一项任务,用户将看不到他想要尽快完成的任务。
    【解决方案3】:

    多线程时计算的影响不会因为您使用 NSThread 而不是 NSOperation 而有所不同。但是请记住,当前的 iOS 设备必须使用双核处理器。

    您的一些问题不是很具体。你可能想也可能不想使用多个 NSOperationQueue。这完全取决于你想如何处理它。如果你有不同的 NSOperation 子类或不同的 NSBlockOperations,你可以通过使用优先级来管理执行顺序,或者你可能希望为不同类型的操作使用不同的队列(尤其是在使用串行队列时)。我个人更喜欢在处理相同类型的操作时使用 1 个操作队列,而在操作不相关/不可靠时使用不同的操作队列。这使我可以灵活地根据发生的事情(网络中断、应用程序进入后台)取消和停止队列中的操作。

    我从来没有找到根据当前操作执行期间发生的事情添加操作的充分理由。如果您需要这样做,您可以使用 NSOperationQueue 的类方法 currentQueue,它会为您提供当前操作所在的操作队列。

    如果您使用 NSOperation 进行核心数据工作,我建议为每个特定操作创建一个上下文。确保在 main 方法中初始化上下文,因为这是您在 NSOperation 后台执行的正确线程上的位置。

    您不一定需要为每个任务拥有一个 NSOperation 对象。您可以下载数据并在 NSOperation 中对其进行解析。您还可以抽象地进行数据下载,并使用 NSOperation 的完成块属性对下载的内容进行数据操作。这将允许您使用相同的对象来获取数据,但有不同的数据操作。

    我的建议是阅读 NSOperation、NSBlockOperation 和 NSOperationQueue 的文档。检查您当前的设计,了解如何使这些类适应您当前的项目。我强烈建议你走 NSOperation 家族而不是 NSThread 家族的路线。

    祝你好运。

    【讨论】:

    • 感谢您的回答。但我没有找到 NSOperation 的 currentQueue 方法。developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
    • @JacksonTale 它是+[NSOperationQueue currentQueue]
    • 啊,谢谢,还以为是在 NSOperation 中。我已经检查了 NSOperationQueue 的文档。
    • 我能知道为每个操作创建一个新的上下文是不是太耗时或太耗费资源?
    • 谢谢@justin,我已经用该方法的正确类编辑了我的回复。杰克逊,一旦你创建了你的核心数据栈,NSManagedObjectContext 对象并不是很昂贵。由于您没有重新创建堆栈,因此性能不会成为问题。在执行实现之前,请阅读 NSManagedObjectContext 的文档。您需要将后台线程上的 NSManagedObjectContext 更改的内容与主线程的 NSManagedObjectContext 合并,以便使用保存的背景信息更新主 NSManagedObjectContext。
    【解决方案4】:

    只是为了添加到@justin 的答案

    如何将 NSOperationQueue 与 CoreData 配合使用?每次我访问 CoreData,我应该创建一个新的上下文吗?会很贵吗?

    在将 NSOperation 与 Core Data 一起使用时,您应该非常小心。
    始终必须记住的是,如果您想在单独的线程上运行 CoreData 操作,您必须为该线程创建一个新的NSManagedObjectContext,并共享主的托管对象上下文持久存储协调器(“主”MOC 是应用程序委托中的那个)。

    此外,非常重要该线程的新托管对象上下文是从该线程创建的。 因此,如果您打算将 Core Data 与 NSOperation 一起使用,请确保在 NSOperation 的 main 方法中初始化新的 MOC,而不是 init

    Here 是一篇关于 Core Data 和线程的非常好的文章

    【讨论】:

    • 谢谢。如果我将所有读/写 CoreData 操作放在一个专用的 NSOperationQueue 中,并将 maxConcurrentCount 设置为 1,我可以只使用一个上下文吗?我可以假设如果我这样设置 NSOperationQueue,它将始终在一个线程中运行?
    • 遗憾的是没有。 NSOperationQueue 管理它自己的线程池,并根据它的工作负载决定如何生成线程。所以简而言之:不,不能保证它总是在同一个线程上运行
    【解决方案5】:

    使用 GCD - 它是一个比 NS* 更好的框架

    将所有 CoreData 访问权保留在一个队列中,并在例程结束时进行 dispatch_async 以保存回您的 CoreData 数据库。

    如果您有开发者帐户,请观看此 WWDC 视频:https://developer.apple.com/videos/wwdc/2012/?id=712

    【讨论】:

    • 这是不准确的。来自文档: 注意:在 iOS 4 及更高版本中,操作队列使用 Grand Central Dispatch 来执行操作。在 iOS 4 之前,它们为非并发操作创建单独的线程,并从当前线程启动并发操作。有关并发和非并发操作之间的区别以及它们如何执行的讨论,请参阅 NSOperation 类参考。
    • 这不是不准确的,它只是更现代和更强大 - 现在 iOS4 之前的相关性要低得多,并且 iOS4 之后的操作队列已被“移植”到 GCD。因此,如果您从头开始 - 理解 GCD 会比理解 NSOperation 更好。
    • 我不确定这是不是真的。 NSOperationQueue 构建在 GCD 之上(至少在较新版本的 iOS 中),它免费为您提供了一些仅使用 GCD(例如依赖项)将是大量工作的东西。一般来说,您应该使用能够完成这项工作的最高级别的框架。
    • 查看 Apple 视频,他们建议使用 GCD 而不是其他任何东西。
    • 这是你不懂的:NSOperationQueue就是GCD。这是一个更高杠杆的框架,可以为你做很多工作。 Apple 推荐 GCD 而不是 NSThread。 NSOperationQueue 是 GCD。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-30
    • 1970-01-01
    相关资源
    最近更新 更多