多个 NSManagedObjectContexts - 防止竞争条件和死锁

Posted

技术标签:

【中文标题】多个 NSManagedObjectContexts - 防止竞争条件和死锁【英文标题】:Multiple NSManagedObjectContexts - preventing race conditions and deadlocks 【发布时间】:2014-12-02 22:54:09 【问题描述】:

我已经阅读了大量关于后台核心数据进程的博客,但我还没有更深入地了解如何最好地管理许多同时触发的 BG 核心数据任务并在未定义的情况下通知主线程 MOC时间。

我知道每个 NSThread 应该有 1 个 NSManagedObjectContext,并且通过订阅 NSManagedObjectContextDidSaveNotification 以及使用 [context performBlock 我可以完成一些不错的异步任务。

也就是说,我正在异步运行很多任务,我不能 100% 知道一些任务何时会重叠,并且我观察到了以下形式的竞争条件......

BG MOC 1 开始执行任务 BG MOC 2 开始执行任务 BG MOC 2 完成任务并发送保存通知 BG MOC 1 完成任务并随后擦除 BG MOC 2s 更改

我的总体问题是如何解决多个 MOC 中的竞争条件?

    如果正确的行为是每个线程有 1 个 MOC。我可以创建一个 NSThread ivar 并将我的所有核心数据工作放在上面吗?这样我就可以拥有一个与自身同步工作的 MOC?

    我读过 NSLock 可能是避免同时从多个线程访问某些代码的解决方案。但我不知道我应该锁定什么?保存上下文方法?持久存储(似乎使多线程毫无意义)?

    最后,我可以标记/编号/命名我的 MOC 吗?这样,如果我知道其他任务正在运行,我可以存储通知并按照它们被实例化的顺序处理它们,以确保没有数据被覆盖?

【问题讨论】:

【参考方案1】:

    建议每个线程一个 MOC。有例外,但一般规则仍然成立。不要创建 NSThread 对象。只是不要。太痛苦了。而是使用块或 NSOperation 实例。它们更容易摸索并保护您免受很多痛苦。

    不要对 Core Data 使用锁。如果使用得当,Core Data 会进行自己的锁定,如果你在周围乱扔锁,就会导致问题。理想情况下,您应该永远在现代 Objective-C 中自己调用 lock。

    除了具有 ivar 或属性引用之外,您不能命名 MOC。你也不应该需要。

使用 Core Data 进行多线程的最简洁方法如下:

您有一个主线程/UI MOC。那是你唯一的真理来源。您的 UI 会从中获取数据并写入数据。 任何后台进程都在 NSOperation 或类似结构中完成。您在此构造中创建一个 MOC,它是主上下文的 上下文。 保存子项时,更改将合并到父项(即 UI MOC)。 设置适合您的主 MOC 的合并策略。如果您认为需要针对不同情况制定不同的政策,那么您应该重新审视自己的工作方式。

理想情况下,每个后台进程都应该是一个数据孤岛,可以独立运行而不会与另一个进程发生冲突。如果您有冲突,那么这是您需要在业务逻辑中解决的合并问题。

如果您遇到两个后台操作将命中同一条数据的情况,那么您应该按顺序运行它们,而不是并行运行。对相同数据的并行编辑是等待发生的痛苦,不要这样做。

您可以通过使用NSOperationQueue 实例来控制事情是顺序的还是并行的。

遵循这些规则,您将不会遇到竞争条件或死锁。

【讨论】:

很好的答案,非常感谢。我没有在我的实现中使用NSOperation / NSOperationQueue,所以我将从那里开始。很高兴对我所有狡猾的想法都坚决反对。 “NSOperation 或类似的构造。你创建一个 MOC 而不是这个构造”应该是“这个构造的内部”吗? 我假设使用 GCD 算作“类似构造”,只要每个 MOC 及其对象只能从其自己的串行队列访问,一切都很好,不是吗? NSOperationQueue. 你说 GCD 是一个“相似的结构”是正确的。然而,NSOperations 有几个好处,包括可维护性(单独的类文件更容易维护)和取消操作的能力。还有其他好处,但这是一个较长的讨论。 "如果您遇到两个后台操作将命中同一条数据的情况,那么您应该按顺序运行它们,而不是并行运行。对同一数据进行并行编辑是等待发生的痛苦,不要这样做。”伟大的洞察力。正在尝试实现这方面。

以上是关于多个 NSManagedObjectContexts - 防止竞争条件和死锁的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:如何将 NSManagedObjectContext 传递到多个视图模型中

NSManagedObjectContext 通过通知中心问题在多个线程上保存/合并

如何在多个线程上处理 NSManagedObjectContext 和 NSManagedObject 创建和编辑?

NSManagedObjectContext 类别

NSManagedObjectContext类别

将块与 NSManagedObjectContext 一起使用 [重复]