Núcleo de datos nesteds contexts de objects gestionados y interlockings frecuentes / congelados

Tengo un problema que es casi idéntico al problema descrito por esta persona aquí, pero no ha recibido respuesta:

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

Aquí está el problema:

Tengo una configuration MOC padre con NSPrivateQueueConcurrencyType y un set de coordinador de tienda persistente, tiene una configuration MOC hijo con NSMainQueueConcurrencyType. La idea de ser la mayor parte del trabajo duro y los ahorros se puede hacer en el MOC privado liberando el hilo principal de bloquear la interfaz de usuario. Lamentablemente, me encuentro en un par de situaciones que causan lockings.

Si el MOC secundario (en el subprocess principal) está realizando una búsqueda con NSFetchedResultsController, el context principal se envía a -executeFetchRequest: puede crear un interlocking. Ambas operaciones se realizan dentro del context de un performBlock: para sus respectivos MOC, aunque los documentos parecen indicar que usar un tipo de concurrency de subprocess principal MOC en el subprocess principal sin performBlock: está bien.

Parece que la queue privada está esperando en el locking de PSC que el context secundario en el hilo principal ya ha bloqueado. Parece que el context secundario (mientras se mantiene el locking de PSC) está tratando de desplazar_synchronization al context principal y, por lo tanto, ambos están esperando el uno al otro.

¿PriveQueue -> MainQueue es una configuration compatible? Parece que la mayoría de las personas todavía tienen el context principal en el hilo principal.

El hilo principal se ve así:

> #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_FulfillDefernetworkingFault () > #9 0x017639f2 in _shanetworkingIMPL_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 

la stack de queue privada se ve así:

 #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 () 

Él también escribe esto:

Estoy empezando a pensar que el problema es NSFetchedResultsController, que siempre está atascado en performFetch: cuando se producen estos interlockings. La mayoría de las veces se quedará atascado tratando de fallar en un object como resultado de preguntar por su nombre de sección. Como testing, intenté reproducir lo que hace el FRC y ejecuté el executeFetchRequest: y luego iteró a través de los resultados preguntando a cada object por su nombre de sección. Y esto no causa un punto muerto. Si dejo el FRC para hacer performFetch: después de hacer mi testing, seguirá en punto muerto allí. Estoy 99% seguro de que el FRC tiene un problema de synchronization con contexts nesteds.

Pregunta: ¿Alguien sabe por qué ocurre este problema? sabes como resolverlo? ¿Esto es un error?

Acabo de leer esta publicación SO donde fabrice truillot de chambrier recomienda no utilizar contexts nesteds en este momento. Él hace reference al artículo Core Data Growing Pains .

De ese artículo:

NSFetchedResultsController deadlocks

Nunca quieres que tu aplicación se interrumpa. Con NSFetchedResultsController y contexts nesteds, es bastante fácil de hacer. Usando la misma configuration de UIManagedDocument descrita anteriormente, ejecutar requestes de recuperación en el context de queue privada mientras usa NSFetchedResultsController con el context de queue principal probablemente se interbloquee. Si comienzas ambas aproximadamente al mismo time, sucede con casi 100% de consistencia. NSFetchedResultsController probablemente esté adquiriendo un locking que no debería ser. Esto se ha reportado como fijo para una próxima versión de iOS.

radar: // 11861499 solucionado en una próxima versión

Esto parece describir exactamente su problema.

Para mi aplicación iOS 6, tengo la misma configuration de concurrency que el OP: un MOC padre que utiliza una queue privada y un MOC hijo en el hilo principal. También tengo un NSFetchedResultsController que utiliza el MOC secundario para actualizar un UITableViewController. Ambos MOC se inicializan en AppDelegate y se utilizarán en toda la aplicación. AppDelegate tiene dos methods, savePrivateThreadMOCToCoreData y saveMainThreadMOCToCoreData , para mantener los cambios en el CD. En el lanzamiento, despacho un inicializador de conetworkingata en una queue privada de la siguiente manera. La idea es dejar al usuario inmediatamente en la vista de tabla y permitir que el inicializador actualice los datos del núcleo en segundo plano.

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

Inicialmente, cuando savePrivateThreadMOCToCoreData hacía salva en un -performBlock, estaba viendo los mismos interlockings psynch_mutex descritos en " Dolores de crecimiento de datos centrales " vinculados anteriormente. También vi fallas si intentaba leer datos en el TableVC mientras el guardado estaba en progreso.

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

Para superarlos, cambié a haciendo guardados con -performBlockAndWait. Dejé de ver los interlockings y los lockings, pero no me pareció correcto hacer que la interfaz de usuario esperara los guardados. Finalmente, eliminé todas las llamadas a -performBlock * y utilicé una vainilla simple [privateMOC save: & error] y así, todos mis problemas desaparecieron. El controller de resultados desencryption lee datos parcialmente guardados de forma limpia y actualiza la tabla, no más interlockings o errores "mutados mientras se enumeran".

Sospecho que -performBlock * se supone que debe ser utilizado por otros hilos, los que no crearon el MOC en cuestión, para solicitar operaciones en él. Dado que mis MOC de hilo principal y privado pertenecen al delegado de la aplicación, las guardadas en el MOC privado no deben usar -performBlock *.

Probablemente sea relevante que aunque mi entorno de compilation sea iOS 6, mi base de implementación se centre en el SDK iOS 5.0. Parece que otros ya no están viendo este problema con iOS 6.

Me pasa porque los padres están configurados con NSMainQueueConcurencyType

Para resolverlo, hago que el object gestionado seacontext para mainQueue sea un elemento secundario. Llamé a restablecer cada vez que quiero cargar cosas para garantizar que los datos en mainQueue sean los mismos con el padre. A menudo no lo es

También tuve un locking relacionado con developerSubmittedBlockToNSManagedObjectContextPerform .

En mi caso, considere el siguiente patrón de llamada de método:

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

donde: A (CDManager.privateContext) llama a B () B () llama a C () C () llama a D ()

y: el método A () y el método C () contienen algunas operaciones de Datos básicos. A () ya tiene el conocimiento en qué context trabajar, pero A () no informa a B () sobre el context y C () tampoco tiene información sobre en qué context trabajar, por lo que C () funciona en context pnetworkingeterminado (principal). y esto provoca el locking debido a datos de forma inconsistente en db.

fix: todos los methods que deben funcionar en operaciones db están parametrizados con el context en el que van a funcionar, excepto D () ya que no es necesario que funcione en la operación db, como:

A (context) llamadas B (context) B (context) llamadas C (context) C (context) llamadas D ()

Solucioné exactamente el mismo problema con los interlockings causados ​​por la extracción simultánea de dos hilos (BG ejecutó fetchRequest disparada, MAIN one realizó la captura de NSFRC). La solución es crear un nuevo context para la operación de synchronization de larga ejecución. No tiene context principal, tiene concurrency tipo NSPrivateQueueConcurrencyType y está directamente vinculado con un PSC común. Después de todo el trabajo de larga ejecución se lleva a cabo dentro de este context en segundo plano, lo mergeChangesFromContextDidSaveNotification y lo mergeChangesFromContextDidSaveNotification con rest astackdos de contexts paralelos mediante el uso de la rutina mergeChangesFromContextDidSaveNotification .

Se implementa una gran solución en Magical Record 3. Vea más información aquí: https://stackoverflow.com/a/25060126/1885326 .

La idea de ser la mayor parte del trabajo arduo y de los ahorros se puede hacer en el MOC privado

¿Cómo implementas esa idea? ¿Utilizas algo como esto:

 - (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 }]; } 

Lo hice arriba y también estuve en punto muerto. Luego traté de no tocar la queue de respaldo del context principal. En su lugar, utilizo GCD simple y simple para hacer las cosas de descarga y manipular los datos del núcleo en el context secundario (en la queue principal). Funciona bien. De esta forma, el context padre parece inútil .. Pero al less no causa interlocking …

 - (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); } 

Solo quería escuchar y aceptar completamente con evitar contexts nesteds. He estado trabajando en iOS 7 con contexts nesteds (hijo principal de queue y padre de queue privada) y NSFetchedResultsControllers y tuve un problema imposible de solucionar. Me cambié al uso de MOC independientes y guardé notifications y el problema desapareció.

Si alguien necesita una guía rápida sobre cómo cambiar su código, esta página tiene un código listo para funcionar (solo ignore la recomendación de context nested):

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