核心数据嵌套托pipe对象上下文和频繁死锁/冻结

我有一个问题,这个人在这里描述的问题几乎相同,但它没有得到回答:

http://www.cocoabuilder.com/archive/cocoa/312683-core-data-nested-managed-object-contexts-and-frequent-deadlocks.html#312683

这是问题:

我有一个使用NSPrivateQueueConcurrencyType和一个持久性存储协调器集的父MOC设置,它有一个带有NSMainQueueConcurrencyType的子MOC设置。 这个想法大部分是长时间的努力和保存,可以在私有MOC上完成,使主线程免于阻塞UI。 不幸的是,我似乎遇到了一些导致死锁的情况。

如果孩子MOC(在主线程上)正在使用NSFetchedResultsController执行一个提取操作,父上下文将被发送一个-executeFetchRequest:它可以创build一个死锁。 这两个操作都是在performBlock的上下文中完成的:对于它们各自的MOC,尽pipe文档似乎表明在主线程上使用主线程并发typesMOC而没有执行块:没问题。

看来专用队列正在等待主线程上的子上下文已经locking的PSC锁。 看起来,子上下文(同时持有PSC锁)正试图dispatch_sync到父上下文,因此它们都在等待对方。

是PriveQueue – > MainQueue支持的configuration? 似乎大多数人仍然在主线程上有父上下文。

主线程如下所示:

> #0 0x960f6c5e in semaphore_wait_trap () > #1 0x04956bb5 in _dispatch_thread_semaphore_wait () > #2 0x04955c8f in _dispatch_barrier_sync_f_slow () > #3 0x04955dea in dispatch_barrier_sync_f () > #4 0x01797de5 in _perform () > #5 0x01798547 in -[NSManagedObjectContext(_NestedContextSupport) newValuesForObjectWithID:withContext:error:] () > #6 0x0176416b in _PFFaultHandlerLookupRow () > #7 0x01763f97 in -[NSFaultHandler fulfillFault:withContext:forIndex:] () > #8 0x01763b75 in _PF_FulfillDeferredFault () > #9 0x017639f2 in _sharedIMPL_pvfk_core () > #10 0x017681a0 in _pvfk_11 () > #11 0x0001b322 in -[FBUser sectionName] at /Users/mlink/Code/x/x/FBUser.m:62 > #12 0x011a8813 in _NSGetUsingKeyValueGetter () > #13 0x017a0652 in -[NSManagedObject valueForKey:] () > #14 0x011ab8d5 in -[NSObject(NSKeyValueCoding) valueForKeyPath:] () > #15 0x01851f72 in -[NSFetchedResultsController(PrivateMethods) _sectionNameForObject:] () > #16 0x01853af6 in -[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:] () > #17 0x01850ea6 in -[NSFetchedResultsController performFetch:] () > #18 0x0003a4fc in __62-[SYFriendsTableViewController updateFetchedResultsController]_block_invoke_0 () > #19 0x01797af3 in developerSubmittedBlockToNSManagedObjectContextPerform () > #20 0x049554f0 in _dispatch_main_queue_callback_4CF () > #21 0x01b3e833 in __CFRunLoopRun () > #22 0x01b3ddb4 in CFRunLoopRunSpecific () > #23 0x01b3dccb in CFRunLoopRunInMode () > #24 0x023d6879 in GSEventRunModal () > #25 0x023d693e in GSEventRun () > #26 0x0089aa9b in UIApplicationMain () > #27 0x00002656 in main at /Users/mlink/Code/x/x/main.mm:16 

私有队列堆栈如下所示:

 #0 0x960f8876 in __psynch_mutexwait () #1 0x97e9e6af in pthread_mutex_lock () #2 0x0172ec22 in -[_PFLock lock] () #3 0x0172ebfa in -[NSPersistentStoreCoordinator lock] () #4 0x01746a8c in -[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore] () #5 0x01745030 in -[NSManagedObjectContext executeFetchRequest:error:] () #6 0x0009d49f in -[NSManagedObjectContext(Additions) executeFetchRequest:] at /Users/mlink/Code/objc/C/C/NSManagedObjectContext+Additions.m:44 #7 0x0002177f in +[FBUser usersForFbids:inManagedObjectContext:] at /Users/mlink/Code/x/x/FBUser.m:435 #8 0x00021fc0 in __77+[FBUser updateUserFromGraphValues:inManagedObjectContext:completionHandler:]_block_invoke_0 at /Users/mlink/Code/x/x/FBUser.m:461 #9 0x0180f9f3 in developerSubmittedBlockToNSManagedObjectContextPerform_privateasync () #10 0x04954ecf in _dispatch_queue_drain () #11 0x04954d28 in _dispatch_queue_invoke () #12 0x049544af in _dispatch_worker_thread2 () #13 0x97ea1b24 in _pthread_wqthread () #14 0x97ea36fe in start_wqthread () 

他还写道:

我开始认为这个问题是NSFetchedResultsController总是卡在performFetch:当这些死锁发生。 大多数情况下,由于询问对象的名称而试图在对象中发生错误时,它会被卡住。 作为一个testing,我尝试重现FRC所做的事情,并执行executeFetchRequest:然后遍历结果,询问每个对象的部分名称。 而这并不会造成僵局。 如果我离开FRC做performFetch:在我做testing之后,它仍然会在那里死锁。 我99%确定FRC与嵌套上下文有同步问题。

问:有人知道为什么会出现这个问题吗? 你知道如何解决吗? 这是一个错误?

我刚刚阅读这个SO发布在truice truillot德chambrierbuild议目前不使用嵌套上下文。 他参考了“ 核心数据增长的痛苦”一文 。

从那篇文章:

NSFetchedResultsController死锁

你永远不希望你的应用程序陷入僵局。 有了NSFetchedResultsController和嵌套的上下文,这很容易做到。 使用上述相同的UIManagedDocument设置,在使用带有主队列上下文的NSFetchedResultsController时,在专用队列上下文中执行提取请求可能会死锁。 如果你在大约同一时间启动它几乎100%的一致性发生。 NSFetchedResultsController可能正在获取它不应该的锁。 这已经被报告为即将发布的iOS版本。

雷达:// 11861499在即将发布的版本中修复

这似乎正好描述你的问题。

对于我的iOS 6应用程序,我具有与OP相同的并发设置 – 在主线程上使用专用队列和子MOC的父MOC。 我也有一个NSFetchedResultsController,它使用子MOC来更新一个UITableViewController。 这两个MOC都在AppDelegate中初始化,并在整个应用程序中使用。 AppDelegate有两个方法savePrivateThreadMOCToCoreDatasaveMainThreadMOCToCoreData ,以保持对CD的更改。 在启动时,我按如下方式在私有队列上发送一个coredata初始值设定项。 这个想法是立即将用户拖放到表视图中,并允许初始化程序在后台更新核心数据。

  dispatch_async(private_queue,^{ [CoreDataInitializer initialize]; }); 

最初,当savePrivateThreadMOCToCoreData正在保存在-performBlock中时,我看到上面链接的“ Core Data Growing Pains ”中描述的psynch_mutex死锁相同。 如果在保存过程中尝试将数据读入TableVC,我也看到了崩溃。

  Collection <__NSCFSet: 0x7d8ea90> was mutated while being enumerated. 

为了克服这些,我切换到使用-performBlockAndWait进行保存。 我停止了看到死锁和崩溃,但使用户界面等待保存是不正确的。 最后,我删除了所有对-performBlock *的调用,并使用一个普通的香草[privateMOC save:&error],就这样,我所有的问题都消失了。 获取的结果控制器干净地读取部分保存的数据并更新表,不再有死锁或“列举时发生变化”错误。

我怀疑-performBlock *应该被其他线程使用,那些没有创buildMOC的线程就可以请求对它进行操作。 由于我的私有和主线程MOC都属于应用程序委托,所以保存在私有MOC上不应该使用-performBlock *。

这可能是相关的,虽然我的构build环境是iOS 6,我的基本部署目标SDK iOS 5.0。 似乎别人没有再看到与iOS 6的这个问题。

这发生在我身上,因为父母是用NSMainQueueConcurencyType设置的

为了解决这个问题,我使mainQueue的managedobjectcontext是一个孩子。 我每次调用reset都要加载东西,以确保mainQueue上的数据与父类相同。 它通常不是。

我也得到了与developerSubmittedBlockToNSManagedObjectContextPerform相关的崩溃。

在我的情况下,请考虑以下方法调用模式:

 [privatecontext performBlock:^{ A(CDManager.privatecontext); }]; 

其中:A(CDManager.privateContext)调用B()B()调用C()C()调用D()

和:方法A()和方法C()包含一些核心数据操作。 A()已经知道要处理哪个上下文,但是A()并不通知B()有关上下文,因此C()也没有任何有关在哪个上下文上工作的信息,因此C默认上下文(主)。 这会导致由于db中的数据不一致导致崩溃。

修复:在db操作上工作的所有方法都是用它们要处理的上下文参数化的,除了D(),因为它不需要在db操作上工作,就像:

(上下文)调用B(上下文)B(上下文)调用C(上下文)C(上下文)调用D()

我解决了同时由两个线程同时抓取引起的死锁(BG执行已触发的fetchRequest,主执行一次NSFRC抓取)导致的死锁。 解决scheme是为长时间运行的同步操作创build一个新的上下文。 它没有父上下文,它具有并发typesNSPrivateQueueConcurrencyType ,它直接与普通的PSC链接。 在所有长时间运行的工作在后台完成后,我将其保存并通过使用mergeChangesFromContextDidSaveNotification例程将其与其余并行上下文堆栈合并。

魔法logging3中实现了一个很好的解决scheme。在这里查看更多信息: https : //stackoverflow.com/a/25060126/1885326 。

私人交通部门可以完成大部分艰苦的工作和节省的想法

你如何实现这个想法? 你用这样的东西:

 - (void)doSomethingWithDocument:(UIManagedDocument *)document { NSManagedObjectContext *parent = document.managedObjectContext.parentContext; [parent performBlock:^{ /* Long and expensive tasks.. execute fetch request on parent context download from remote server */ // save document }]; } 

我做了以上,也陷入僵局。 然后我试图不去触摸父上下文的后备队列。 相反,我使用普通和简单的GCD来做下载的东西,并在子上下文(在主队列上)操作核心数据。 它工作正常。 通过这种方式,父上下文似乎没用。但至less它不会导致死锁。

 - (void)doSomethingWithDocument:(UIManagedDocument *)document { dispatch_queue_t fetchQ = dispatch_queue_create("Flickr fetcher", NULL); dispatch_async(fetchQ, ^{ // download from remote server // perform in the NSMOC's safe thread (main thread) [document.managedObjectContext performBlock:^{ // execute fetch request on parent context // save document }]; }); dispatch_release(fetchQ); } 

我只是想要嵌套,并完全同意避免嵌套的上下文。 我一直在iOS 7的嵌套上下文(主队列子和私人队列父)和NSFetchedResultsControllers工作,并有一个不可能解决死锁问题。 我切换到使用独立的MOC和保存通知,问题消失。

如果任何人需要快速指导如何改变他们的代码,这个页面有准备好的代码(只是忽略嵌套的上下文build议):

http://www.cocoanetics.com/2012/07/multi-context-coredata/