2012-08-28 132 views
2

我正在通过新创建的NSManagedObjectContext在后台线程(使用GCD)上执行一个代价高昂的获取(约5秒,约30,000个对象)由后台线程拥有。主线程在后台线程执行时等待[NSManagedObjectContext(_NSInternalAdditions)lockObjectStore]

但是,我没有得到在后台执行此操作的好处,因为主线程正在等待持久存储上的锁定并因此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进入状态,它没有未决的改变之前,我设置了蓝牙功能关闭做的获取,在希望它不会需要在_processReferenceQueue中做任何需要商店锁定的工作。

在开始BT之前,我注意到在[MOC updatedObjects]集合中有一个对象。插入或删除的组中没有对象。

在调用[MOC save]之后,[MOC updatedObjects]集合就像预期的那样是空的。
但是,一旦我开始BT,MT仍然试图锁定店内_processReferenceQueue,尽管没有什么在MOC中应该是脏的。

我试过的下一件事(严格来说就是测试)是在开始BT之前调用[MOC reset]。如预期的那样,[MOC updatedObjects]集合在重置之后也是空的。在代码中的这一点上,我没有触及MT上的任何托管对象,直到BT完成其工作(所以我没有遇到任何问题,因为重置使我已经引用的托管对象无效)。然而,这一次,MT确实不是试图锁定_processReferenceQueue中的持久存储。

这种行为暗示了我在启动BT后没有对MT上的MOC进行任何明确的操作。否则,MT会在_processReferenceQueue内部或外部的某个点(读取或写入)请求锁定。但事实并非如此。

我不确定为什么最近保存的MOC需要后续锁定_processReferenceQueue而最近重置的MOC不需要。

我会继续挖掘。

谢谢! 布赖恩

+0

我有一个想法,但今天通过回答一个缺乏信息的问题已经烧了两次。请在后台线程中发布更多关于你在做什么的信息,以及你在主线程中做什么(例如,你是否在后台执行任何写操作,主线程中是否有FRC)。 ..等 –

+0

另外,请记住,您不能跨线程传递提取的对象。在后台使用这些30k对象,或传递其ID。 –

+0

以上更新。此外,即使我仅仅在背景上执行提取并忽略结果(作为测试),MT上的锁定也会发生。我确实将objectID传回MT。 – BrianJStafford

回答

2

没有更多的信息,我所能做的就是猜测。

不幸的是,核心数据并未使用相同的持久存储协调器为MOC实现读写器锁定。这意味着在一个线程上读取会阻塞使用相同持久存储协调器的其他线程。

因此,您在后台长时间执行的读取操作将阻止主线程读取。

你有几种选择。

分解长时间运行的提取,以便按顺序提取小块。您想要执行多个小提取操作,而不是在完成当前操作之前发出下一个小提取操作。

这将允许在主线程上获取一个机会在后台线程上的多个小提取之间进行处理。

另一种方法是创建一个单独的持久存储协调器,将您的后台MOC连接到该存储并执行您的抓取。如果您使用多个持久性存储协调器,则可以同时拥有多个读取器,并且它们不会永久阻塞对方。

编辑

我肯定想过打破它。但这不是 的情况,我试图让两个同时取出很好地012.一起玩。 MT在这一点上不应该接触Core Data。 - BrianJStafford

您是否使用UIManagedDocument,或已创建的主线程在MOC,或NSMainQueueConcurrencyType

我问,因为核心数据确保所有“用户事件”每次通过运行循环处理。我从来没有深入研究过如何完成这项工作,但我想他们会在主线程上为MOC的运行循环添加一个处理程序。

核心数据死了很简单,除非你有多个MOC。然后,交互非常复杂,以至于很难确定没有实际代码的情况。

如果可以,请发布创建任何/所有MOC的代码以及使用这些MOC的代码。

我真诚地怀疑,如果与当前上下文无关,那么他们会在运行循环处理中实际获得对商店的锁定,因此可能会与后台MOC进行一些隐藏的交互。我敢打赌,你的应用程序中仍然存在一些你不认为你正在做的事情,那就是与主要的MOC进行背景调查。

如果您愿意,您可以轻松追踪[_PFManagedObjectReferenceQueue _processReferenceQueue:]的调用方式。

测试1.创建一个简单的项目,检查Core Data框以获取简单的Core Data模板项目。请在_processReferenceQueue:处设置一个断点,以确保您可以获得该断点。

测试2.注释掉实例化核心数据内容的部分,并查看是否通过链接到框架来触发断点。

测试3.创建MOC,但不要做其他任何事情。只需创建它。重复看看你是否达到了断点。

测试4.摆脱“默认MOC”的,所以你基本上是喜欢Test 2.现在,创建一个私有队列MOC,通过performBlock用它做一个简单的交易,看看你是否命中断点。

你可以看到这是怎么回事。基本上,确定非常简单的用例组合导致您在主线程中打断点

至少会让你知道框架本身是否会导致这种情况发生,这反过来会让你知道你的复杂应用正在做什么。

编辑

好奇心得到了我的好。我只是跑了几个简单的测试,在两个设置断点:

-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore] 
-[_PFManagedObjectReferenceQueue _processReferenceQueue:] 

我看不出来,除非我实际上搞乱了MOC。每个MOC都是限制并发类型。我创建了一个拥有100,000个简单实体的数据库

主线程使用FRC加载数据。我添加了一个按钮,当按下该按钮时,会启动一个dispatch_async并获取所有100,000个对象。

我在补充线程中看到两个断点,但在主线程中都没有命中。如果我在主线程上获取或更新MOC,当然,我会打到这些断点。

我在模拟器上运行。

所以,我的结论是,CoreData本身并不负责你所看到的行为。

可能是由于MOC上的某些配置,持久性商店协调员,持久存储或您的上下文之间的某些未知交互。

没有关于对象和实际代码配置的进一步信息,我最好的结论是你在代码中做了“某些事情”,导致这种情况发生。

您应该设置这些断点,并查看您的代码在命中时发生了什么。

+0

我绝对想过要分手。但是这并不是我想让两个同时抓取很好地结合在一起的情况。 MT在这一点上不应该接触Core Data。 – BrianJStafford

+0

您是否使用UIManagedDocument?您是否在主线程或NSMainQueueConcurrencyType上创建了MOC? –

+0

我没有使用UIManagedDocument。 MT上有一个默认并发类型(NSConfinementConcurrencyType)的MOC。后台线程有其自己的MOC,也有默认的并发类型。 – BrianJStafford