【发布时间】:2012-08-28 23:03:41
【问题描述】:
我正在后台线程(使用 GCD)上通过后台线程拥有的新创建的 NSManagedObjectContext 执行昂贵的获取(约 5 秒,大约 30,000 个对象)。
但是,我并没有在后台执行此操作,因为主线程正在等待持久存储上的锁定,因此 UI 被冻结。这是堆栈跟踪:
* thread #1: tid = 0x1c03, 0x3641be78 CoreData`-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore], stop reason = breakpoint 1.1
frame #0: 0x3641be78 CoreData`-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore]
frame #1: 0x36432f06 CoreData`-[_PFManagedObjectReferenceQueue _processReferenceQueue:] + 1754
frame #2: 0x36435fd6 CoreData`_performRunLoopAction + 202
frame #3: 0x35ab9b1a CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 18
frame #4: 0x35ab7d56 CoreFoundation`__CFRunLoopDoObservers + 258
frame #5: 0x35ab80b0 CoreFoundation`__CFRunLoopRun + 760
frame #6: 0x35a3b4a4 CoreFoundation`CFRunLoopRunSpecific + 300
frame #7: 0x35a3b36c CoreFoundation`CFRunLoopRunInMode + 104
frame #8: 0x376d7438 GraphicsServices`GSEventRunModal + 136
frame #9: 0x33547cd4 UIKit`UIApplicationMain + 1080
frame #10: 0x000f337a MyApp`main + 90 at main.m:16
我(相信)我已经确认我没有在后台线程工作时访问主线程的 NSManagedObjectContext。从堆栈跟踪中,很明显我没有直接对 Core Data 做任何事情。但是某些事情触发了对 _processReferenceQueue: 的调用,这导致尝试锁定存储。有谁碰巧知道这个方法的作用以及我如何/如果我可以在我的后台线程正在执行它的工作时阻止它被调用?
编辑
在我开始后台获取之后,我没有在主线程上进行任何核心数据读取或写入。这就是为什么这很令人费解。如果主线程也试图做更多的工作,我希望会有争执,但事实并非如此——至少,我没有要求它这样做。没有读取,没有写入,没有 FRC。这就是为什么我想知道是否有人熟悉这个 _processReferenceQueue 方法。为什么会被调用?我能做什么导致它运行?
编辑
作为测试,我试图让 MT 的 MOC 进入一个状态,在我关闭 BT 进行提取之前,它没有待处理的更改,希望它不需要在 @987654322 中做任何工作@ 这将需要对商店进行锁定。
在开始 BT 之前,我注意到 [MOC updatedObjects] 集合中有一个对象。插入或删除的集合中没有对象。
调用[MOC save] 后,[MOC updatedObjects] 集合为空,正如预期的那样。
但是,一旦我启动了 BT,MT 仍然试图将商店锁定在 _processReferenceQueue 内,即使在 MOC 中没有什么应该是脏的。
我尝试的下一件事(严格作为测试)是在启动 BT 之前调用 [MOC reset]。同样,[MOC updatedObjects] 集在重置后为空,正如预期的那样。在代码中的这一点上,在 BT 完成其工作之前,我不会接触 MT 上的任何托管对象(因此,由于重置使我已经引用的托管对象无效,我不会遇到任何问题)。然而这一次,MTNOT尝试将持久性存储锁定在_processReferenceQueue。
这种行为向我表明,在我启动 BT 后,我没有对 MT 上的 MOC 做任何明确的事情。否则,MT 将在_processReferenceQueue 内部或外部的某个时间点(用于读取或写入)请求锁定。但它没有。
我不确定为什么最近保存的 MOC 需要随后锁定 _processReferenceQueue 而最近重置的 MOC 不需要。
我会继续挖掘。
谢谢! 布赖恩
【问题讨论】:
-
我有一个想法,但是今天因为回答一个信息不足的问题而被烧了两次。请发布更多信息,具体说明您在后台线程中正在做什么,以及您在主线程中正在做什么(即,您是否在后台进行任何写入,您是否在主线程上有 FRC)。 ..等
-
另外,请记住,您不能跨线程传递获取的对象。在后台使用这 30k 个对象,或传递它们的 ID。
-
以上更新。此外,即使我只是在后台进行提取并忽略结果(作为测试),MT 上的锁定也会发生。我确实将 objectID 传递回 MT。
-
最后一部分...你重置 MOC 的地方。在那之前它的状态是什么?尽可能转储所有属性。
-
很好的建议。我查看了保存前后以及重置前后的 MOC。在这两种情况下,之前的状态似乎是相同的。我在 after 状态中看到的唯一区别是重置后 _cd_rc ivar 仍为 0,而在保存后它变为 2。我不确定那个变量是什么,但我想知道保存后它的非零值是否是 _processReferenceQueue 需要对存储进行锁定的原因。
标签: iphone ios multithreading core-data