核心数据:大后台抓取能否阻塞主线程抓取请求?

Posted

技术标签:

【中文标题】核心数据:大后台抓取能否阻塞主线程抓取请求?【英文标题】:Core Data: Can a Large Background Fetch Block Fetch Request on the Main Thread? 【发布时间】:2011-09-19 12:58:28 【问题描述】:

我有一个 iPad 应用程序,它在 Core Data 中有相当多的文本数据(大约 75MB)。每周都会导入新数据。导入发生在具有自己的 NSManagedObjectContext 甚至自己的 NSPersistentStoreCoordinator 的辅助线程上。

导入过程包含许多提取,除了一个之外,所有这些都非常有效,因为它返回大量记录(这可能可以稍微优化一点,但实际上并非如此。)不幸的是,在此期间进行大量抓取,主 UI 被阻塞(因为它还必须从商店抓取并且抓取被延迟)。

我已经在各个点测试了导入过程,以确认它确实在后台线程上。我已经仔细检查了代码以确保使用具有单独协调器的导入上下文。 undomanager 为 nil,并且导入上下文经常被重置。

是否有可能即使使用单独的 NSPersistentStoreCoordinator,后台的大型 fetch 请求仍会阻止主线程上的 fetch 请求,还是我做错了什么?

【问题讨论】:

【参考方案1】:

后台线程上的操作不能阻塞主线程,但它会消耗大量内存,以至于主线程可以更有效地运行。

听起来您“返回大量记录”的大量后台获取正在创建内存中活动对象的大型对象图,这正在消耗您的内存。低内存阻碍了主线程。

使用仪器分析您的记忆以进行确认。

解决方案是在内存中只保留你绝对必须的实际活动的、填充的对象。您可以使用较小的提取、作为故障提取、提取属性或作为 managedObjectID 提取以及类似方法来减少内存占用。

此外,请确保下载数据本身的网络操作不会占用内存或周期。

【讨论】:

谢谢,我将分析内存使用情况,但我应该注意,即使在设备上运行后台线程时,我也没有收到任何内存警告。我昨天看到了一些代码(cimgf.com/2011/08/22/…),其中在后台线程上使用了 NSRunLoop 的 runUntilDate 方法,以确保主线程保持平稳运行。我可以试试。 在你摆弄运行循环之前优化你的提取和它们的内存使用。【参考方案2】:

听起来您已经在正确处理线程(尽管从技术上讲,您并不需要每个线程一个新的 NSPersistentStoreCoordinator,只要每个线程都有一个 NSManagedObjectContext)。

您的大量获取听起来像是问题;你看过 NSFetchedResultsController 来批处理结果吗?

如果您已经有了,那么您需要找到一些优化查询的方法 - 您用于大型提取的 NSPredicate 是什么?


另一种方法可能是将您的文本数据卸载到平面文件中,并将文件名保留在核心数据中 - 这将减少您需要写入的数据量,从而在更短的时间内阻塞其他线程。

您可以向您的 NSManagedObject 子类添加一个方法,根据请求从文件中获取文本 - 您的 UI 不应该知道您是如何存储文本的!

【讨论】:

您好,感谢您的回复。我不能使用 NSFetchedResultsController 因为我没有填充表,即使这样,主线程上的获取请求也很小。大获取请求是 [NSPredicate predicateWithFormat:@"word IN %@", wordSet];其中 wordset 包含 NSStrings。该请求还预取了一个关系(不预取会使性能降低大约 20 倍) 您不仅需要将 NSFetchedResultsController 用于表格,还可以将其用于任何您想要的东西;)但是您是对的,您的问题是在 sqlite 中进行全文搜索 - 这是 永远不会像你这样做的那样快:) 你需要开始查看倒排索引 (en.wikipedia.org/wiki/Inverted_index) 或查找表之类的东西 - 这是一个非常复杂的问题! 感谢您的帮助,但我已经使用了倒排索引。但是,我认为我们已经跑题了 - 我需要了解的第一件事是后台线程上的单独协调器是否会阻塞主线程上的协调器,这似乎正在发生 理论上不会;它根本不应该阻塞主线程:) 显然在 SQLite 中存在某种程度的阻塞,如果在 Core Data 中没有的话。如果 SQLite 设计得很好,它应该只会在短时间内阻塞,但我不知道。【参考方案3】:

因为它还必须从商店中提取并且提取被延迟

这正是罪魁祸首,因此,根据您的情况,答案可能是确定的。具体来说,如果您的大量提取与任何其他提取发生在同一存储中,则后续提取将被阻止,直到第一个提取完成。

在这种情况下,一种策略是使用 returnsObjectsAsFaults = NO 预提取您在大规模提取期间可能使用的任何对象。

是否有可能即使使用单独的 NSPersistentStoreCoordinator,后台的大型 fetch 请求仍然可以阻止主线程上的 fetch 请求

在这种情况下,它不应该阻塞 - 但正如您在问题中指出的那样,还有其他可能导致阻塞的因素。此解决方案有其自身的问题:整合更改,但如果您可以合并两个协调器,那么它原则上可以工作。

【讨论】:

以上是关于核心数据:大后台抓取能否阻塞主线程抓取请求?的主要内容,如果未能解决你的问题,请参考以下文章

核心数据:后台抓取

从后台线程调用 startActivity 并且主线程被阻塞时,Activity 延迟启动

核心数据-后台线程中的更新实体会自动更改主线程中的 NSManagedObject 而无需合并-为啥?

优化 NSFetchedResultsController 抓取

本机调用阻塞主线程

多线程抓取异常