【问题标题】:Core data multithreading performance核心数据多线程性能
【发布时间】:2014-01-16 10:49:59
【问题描述】:

我正在开发一个使用 Core Data 进行内部存储的应用程序。此应用程序具有以下功能:

  1. 通过下载并解析大型 XML 文件,然后将条目与核心数据一起保存,从而与服务器同步数据。
  2. 允许用户进行提取(大数据提取)和 CRUD 操作。

我已经阅读了很多文档,其中有几种模式可以遵循,以便将多线程与 Core Data 一起应用:

  1. 嵌套上下文:这种模式似乎存在许多性能问题(子上下文在进行提取时会阻塞祖先)。
  2. 使用一个主线程上下文和后台工作者上下文。
  3. 使用单个上下文(主线程上下文)并通过 GCD 应用多线程。

我尝试了上面提到的 3 种方法,发现最后 2 种可以正常工作。但是,在谈论性能时,我不确定这些方法是否正确。

是否有一个众所周知的性能模式可以应用,以制作一个实现上述功能的健壮应用程序?

【问题讨论】:

  • 你看过神奇的唱片吗?
  • 是的,我有,但我必须直接使用 Core Data,而不使用任何外部库。
  • 您是否评估了一个两阶段堆栈,其中 "root context" 使用私有队列,而 "main context" 与之关联到使用“根上下文”作为其父级的主线程(主队列)。任何其他附加上下文(如果需要)可以使用私有队列并使用“主上下文”或“根上下文”作为其父级。
  • @CouchDeveloper 我通过创建一个“根上下文”(NSPrivateQueueConcurrencyType)、一个“主上下文”(NSPMainQueueConcurrencyType)尝试了两个堆栈阶段,其中“根上下文”作为父上下文和“附加上下文”(NSPrivateQueueConcurrencyType ) 将“主要上下文”作为父级。这种方法显示了很多问题(即:在“附加上下文”中执行大量提取时,“主上下文”被阻止,因此 UI)。
  • @CouchDeveloper 检查此链接wbyoung.tumblr.com/post/27851725562/core-data-growing-pains。它说你提到的方法有很多后备。

标签: ios iphone multithreading core-data


【解决方案1】:

罗克里迪,

在我的 Twitter iOS 应用程序 Retweever 和 #chat 中,我使用了一个简单的两个 MOC 模型。所有数据库插入和删除都在私有并发insertionMOC 上进行。主 MOC 通过来自insertionMOC-save: 通知合并,并在合并处理期间发出自定义 UI 更新通知。这让我可以分阶段工作。进入应用程序的所有推文都在后台处理,并在一切完成后呈现给 UI。

如果您下载应用程序,#chat 的引擎已经过现代化改造,并且比 Retweever 的引擎更高效且与主线程更加隔离。

匿名, 安德鲁

【讨论】:

  • 感谢您的回复。这就是我打算实现的,只是我对 UI 刷新有点困惑。您是依赖 NSFetchedResultsController 还是使用私有并发 MOC 执行提取。如果我想获取大量数据,我应该怎么做?每次我想刷新我的 UI 时,我都会实例化一个私有并发 MOC 吗?
  • 我举个例子。假设我从服务器获取一个大的 XML 文件,在解析它之后,我使用 Core Data 保存条目。当该过程结束时,主 MOC 合并。现在假设我必须刷新一些 UI(TableView ...),或者我需要搜索一些条目,但搜索或 UI 刷新操作可能很繁重。我是使用主 MOC 还是使用另一个私有并发 MOC(工人 MOC)?现在我正在使用单个私有并发 MOC、单个主线程 MOC 和工人 MOC(工人 MOC 执行大提取以更新 UI 例如)。
  • 我使用获取的结果控制器和自定义控制器来寻找合并通知。 iOS 要求您始终使用主 MOC 更新主线程上的 UI。在后台进行大型更新的挑战在于它们也作为大型更新呈现给主线程。这可能需要一些时间。
  • 在将主 MOC 与后台 MOC 合并时,我遇到了获取结果控制器的问题。当合并的数据太大时,合并会导致 UI 阻塞。
  • 是的,每当您进行大量工作并合并到主 MOC 时,您都会阻塞您的 UI。答案?每次保存时做更少的工作。我还在主线程上排队工作,并在更合适的时间进行。管理更新时间非常重要。
【解决方案2】:

Apple 建议为每个线程使用单独的上下文。

推荐使用 Core Data 进行并发编程的模式是 线程限制:每个线程必须有自己的完全私有的 托管对象上下文。有两种可能的方式来采用 模式:为每个线程创建一个单独的托管对象上下文 并共享一个持久存储协调器。这是 通常推荐的方法。创建单独的托管对象 每个线程的上下文和持久存储协调器。这 方法以牺牲更大的代价提供更大的并发性 复杂性(特别是如果您需要在 不同的上下文)和增加的内存使用量。

看苹果Documentation

【讨论】:

  • 我不认为这是推荐的方法了(即,在线程限制策略中使用 threads)。我建议分别使用基于队列(而不是线程)的NSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType,以及分别使用performBlockperformBlockAndWait
【解决方案3】:

根据苹果文档,使用线程限制来支持并发 每个线程创建一个托管对象上下文。它会让你的生活变得轻松。这适用于在后台解析大数据并在主线程上获取数据以显示在 UI 中的情况。

关于合并问题,有一些最好的方法来解决它们。 永远不要在线程之间传递对象,而是在必要时将对象 id 传递给其他线程并从该线程访问它们,例如,当您通过解析 xml 保存数据时,您应该将它们保存在当前线程 moc 上并获取它们的 id,然后传递给UI 线程,在 UI 线程中重新获取它们。

您也可以注册通知,当一个 moc 发生变化时,您将收到用户信息字典的通知,该字典将更新对象,您可以将它们传递给合并上下文方法调用。

【讨论】:

  • 假设我没有使用 NSFetchResultsController,我是否需要创建单个托管对象上下文 (NSMainQueueConcurrencyType)?现在我使用三种类型的托管对象上下文:一种用于导入数据,一种用于主线程,另一种是工作线程(长提取、插入、删除、更新)。在这种情况下,主线程托管对象上下文有用吗?
  • 我们是否使用 NSFetchResultController 不仅取决于天气。在线程中使用单独的上下文总是更好。在不同的线程中使用单个上下文会大量使用互斥锁,这将使您的应用程序变慢,在您的情况下,如果您对所有线程使用一个上下文并且您开始解析大文件,则导入的线程将获得对您的上下文的锁定,以及其他两个线程必须等待。所以最好为不同的线程创建不同的上下文。创建过多的上下文,维护起来也很痛苦,您必须关注数据的完整性。
  • 在您的情况下,如果工作线程有很多工作要做,那么最好为 main 创建单独的线程,这将使您的 UI 流畅。
  • 所以在这种情况下,我应该为主线程创建一个上下文,一个用于与服务器同步的上下文以及一个用于繁重操作的工作上下文。我说的对吗?
猜你喜欢
  • 2013-03-02
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多