来自 2 个串行队列的 NSManagedObjectContext 死锁

Posted

技术标签:

【中文标题】来自 2 个串行队列的 NSManagedObjectContext 死锁【英文标题】:NSManagedObjectContext deadlocking from 2 serial queues 【发布时间】:2012-03-16 10:32:49 【问题描述】:

我创建了一个系统,我可以从一个单例对象请求一个 NSManagedObjectContext,这取决于它运行的队列。每个串行 GCD 调度队列都与某个任务相关联,因此可以获得自己的上下文,尽管它们都具有相同的持久存储协调器。

我假设这将解决我与线程相关的问题,到目前为止它似乎已经解决了,但现在我有一个不同的问题:如果 2 个串行队列,具有不同的 MOC,都尝试创建上下文执行,它们都锁定并且应用程序冻结。那我错过了什么?

"...[I]如果您为每个线程创建一个上下文,但都指向同一个持久存储协调器,Core Data 会以线程安全的方式访问协调器(NSManagedObjectContext 的锁定和解锁方法)处理递归)。” (source)

我在那里读到的是,Core Data 应该使用我的设置正确处理锁定和解锁。或者在这种情况下我是否理解“以线程安全的方式”错误?

编辑:我基本上有一个将队列映射到上下文的字典。起初我想使用线程而不是队列,直到我读到这部分:

“注意:您可以使用线程、串行操作队列或调度队列来实现并发性。为简洁起见,本文始终使用“线程”来指代其中的任何一个。” (source)

【问题讨论】:

【参考方案1】:

如果您所说的“串行队列”是指 GCD 调度队列或NSOperationQueue,则您错误地假设每个队列都有一个专用线程,或者每个队列的任务总是在同一个线程上运行。

您需要找出一种将线程映射到托管对象上下文的方法,可能是通过 NSDictionary,当您在队列上运行任务时,获取与当前线程关联的 MOC。

【讨论】:

我所做的是你所建议的,但使用 GCD 调度队列而不是线程。另请参阅我的上次编辑。 @Erik:您说您将队列映射到上下文。您应该将每个线程映射到上下文 IMO。 这是我的第一个假设,但根据该报价队列也可以正常工作,所以这就是我想知道的原因。今天晚些时候我将尝试重写映射方法,以便它使用线程而不是队列。 顺便说一下,如果我暂停并检查堆栈跟踪,它们是在 2 个不同线程上运行的 2 个不同队列。我知道一般情况下不一定是这样,但是为什么会在这种情况下锁定呢?【参考方案2】:

JeremyP 是对的:队列不 == 线程。队列可以为每个操作创建一个新线程 - Core Data(在默认模式下)需要线程限制(即,创建NSManagedObjectContext 的线程必须是用于从该上下文访问任何对象的线程)。

您可能想要检查限制选项的使用方式 - 如果您只针对 ios5,您可能可以轻松更改它并仍然使用队列。

【讨论】:

是的,我注意到一个通过队列限制的选项。我现在采用的方法可能不如我想象的系统快,但更安全;一个获取上下文,每当保存上下文保存时就会更新。唯一的问题是我必须在传递之前制作 ManagedObject 的副本,因为在我的其他对象完成之前它可能会失效......

以上是关于来自 2 个串行队列的 NSManagedObjectContext 死锁的主要内容,如果未能解决你的问题,请参考以下文章

如何实现串行网络调用队列然后在 RxSwift 中处理?

iOS多线程——同步异步串行并行

GCD 异步串行队列 - 可以限制队列大小?

如何同步 URLSession 任务的串行队列?

ios多线程操作—— GCD串行队列与并发队列

GCD 串行队列调度异步和同步