28

que tienen un problema que es casi idéntico al problema descrito por esta persona aquí, pero no ha respondido a obtener:de Datos Básicos anidada logró contextos de objetos y puntos muertos frecuentes/congela

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

Aquí está el problema:

tengo una configuración MOC padre con NSPrivateQueueConcurrencyType y un conjunto coordinador de almacén persistente, tiene una configuración MOC niño con NSMainQueueConcurrencyType. La idea de que la mayor parte del trabajo arduo y largo se ahorre se puede hacer en el MOC privado, lo que libera al hilo principal del bloqueo de la IU. Desafortunadamente, parece que me encuentro con un par de situaciones que causan bloqueos.

Si el MOC secundario (en el hilo principal) realiza una búsqueda con NSFetchedResultsController, el contexto principal se envía a -executeFetchRequest: puede crear un interbloqueo. Ambas operaciones se realizan dentro del contexto de un performBlock: para sus respectivos MOC aunque los documentos parecen indicar que el uso de un tipo de concurrencia de subproceso principal MOC en el subproceso principal sin performBlock: está bien.

Parece que la cola privada está esperando en el bloqueo de PSCs que el contexto secundario en el hilo principal ya ha bloqueado. Parece que el contexto secundario (mientras mantiene el bloqueo de los PSC) está intentando enviar el sincronizador al contexto primario y, por lo tanto, ambos están esperando el uno al otro.

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

El hilo principal es el siguiente:

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

la pila de espera 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() 

también escribe esto:

estoy empezando a pensar el problema es con NSFetchedResultsController que siempre está bloqueado en performFetch: cuando se producen estos bloqueos. La mayoría de las veces estará atascado intentando fallar en un objeto como resultado de pedir su nombre de sección. Como prueba, traté de reproducir lo que hace el FRC y realicé el executeFetchRequest: y luego repetí los resultados preguntando a cada objeto por su nombre de sección. Y esto no causa un punto muerto. Si dejo el FRC para realizar la función Obtener: después de hacer mi prueba, todavía se estancará allí. Estoy 99% seguro de que el FRC tiene un problema de sincronización con contextos anidados.

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

+0

¿Envías '-executeFetchRequest' al contexto principal? ¿Por qué? –

+0

Como el hilo de fondo (MOC de fondo/padre) está sincronizando los datos hacia un servidor, estoy haciendo una solicitud de búsqueda para descubrir qué objetos necesitan sincronizarse. – thejaz

+2

+1 mismo problema. Para contextos anidados, estoy usando contextos de almacenamiento principal y de raíz de magicalrecord. Antes de llamar a FRC performFetch, inicié una solicitud de sincronización al servidor. El código se congela en performFetch. – angelokh

Respuesta

33

Acabo de leer this SO posting donde fabrice truillot de chambrier recomienda no utilizar contextos anidados en la actualidad. Él da una referencia al artículo Core Data Growing Pains .

A partir de ese artículo:

NSFetchedResultsController deadlocks

You never want your application to deadlock. With NSFetchedResultsController and nested contexts, it’s pretty easy to do. Using the same UIManagedDocument setup described above, executing fetch requests in the private queue context while using NSFetchedResultsController with the main queue context will likely deadlock. If you start both at about the same time it happens with almost 100% consistency. NSFetchedResultsController is probably acquiring a lock that it shouldn’t be. This has been reported as fixed for an upcoming release of iOS.

radar://11861499 fixed in an upcoming release

Esto parece describir exactamente su problema.

+0

¡Muchas gracias! ¡Muchos de mis problemas y sospechas recientes son confirmados por el artículo al que está enlazando! – thejaz

+0

De nada. Ese artículo es realmente informativo. Deberíamos recomendar a fabrice por dar la referencia. –

+0

¡vota tu voto! Regresaré a contextos regulares con tiendas persistentes y espero que desaparezcan todos los congelamientos y bloqueos. – thejaz

-1

The idea being most of the long hard work and saves can be done on the private MOC

¿Cómo implementar esa idea? ¿Utiliza 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 me empantanó. Luego traté de no tocar la cola de respaldo del contexto principal. En cambio, uso GCD simple y simple para hacer las descargas y manipular los datos centrales en el contexto del niño (en la cola principal). Funciona bien. De esta manera los padres contexto parece inútil .. Pero, al menos, que no causa estancamiento ..

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

document.managedObjectContext.parentContext perform block es una mala idea. Todas las cosas relacionadas con Tableview fallan. Por ejemplo, obtiene este error: Error de aserción en [UITableView _endCellAnimationsWithContext: – coolcool1994

+0

Esto es en realidad un problema de fuente de datos de tabla en lugar de un problema de datos centrales. Las transiciones de estado que estás haciendo no tienen sentido para el motor de estado de la tabla. Esto se debe a menudo a que la fuente de datos no se implementa correctamente (por ejemplo, en el estado vacío que devuelve 0 secciones, luego algo inserta una sección ... boom). – quellish

2

Para mis aplicación iOS 6, tengo la misma configuración que la concurrencia OP - MOC una matriz utilizando una cola privada y un niño MOC en el hilo principal. También tengo un NSFetchedResultsController que usa el MOC hijo para actualizar un UITableViewController. Ambos MOC se inicializan en AppDelegate y se utilizarán en toda la aplicación. AppDelegate tiene dos métodos, savePrivateThreadMOCToCoreData y saveMainThreadMOCToCoreData, para conservar los cambios en el CD. En el lanzamiento, distribuyo un inicializador de coredatos en una cola privada de la siguiente manera. La idea es colocar inmediatamente al usuario 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 estaba haciendo paradas en un -performBlock, que estaba viendo los mismos puntos muertos psynch_mutex descritos en "Core Data Growing Pains" vinculado anteriormente. También vi bloqueos si intenté leer datos en TableVC mientras estaba en curso el guardado.

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

Para solucionar esto, cambié a hacer guardias utilizando -performBlockAndWait. Dejé de ver bloqueos y bloqueos, pero no me pareció bien hacer que la IU aguardara a guardar. Finalmente, eliminé todas las llamadas a -performBlock * y usé un simple vainilla [saveMOC privado: & error] y así desaparecieron todos mis problemas. El controlador de resultados obtenido lee los datos parcialmente guardados limpiamente y actualiza la tabla, no hay más bloqueos o errores "mutados mientras se enumeran".

sospecho -performBlock se supone * para ser utilizado por otros hilos, los cuales no creó el MOC en cuestión, para solicitar operaciones en él. Dado que tanto el MOC de mi hilo privado como el principal pertenecen al delegado de la aplicación, los salvados en el MOC privado no deberían usar -performBlock *.

Probablemente sea relevante que aunque mi entorno de compilación es iOS 6, mi objetivo de implementación base es SDK iOS 5.0. Parece que otros ya no están viendo este problema con iOS 6.

+0

Sospeché lo mismo pero no obtuve ninguna prueba. –

0

Me sucede porque los padres están dispuestas con NSMainQueueConcurencyType

Para resolver ese hago la managedobjectcontext para mainQueue a ser un niño. Llamé a restablecer cada vez que quiero cargar cosas para garantizar que los datos en mainQueue sean los mismos con los padres. A menudo no lo es.

-1

Solo quería entrar y aceptar completamente evitar los contextos anidados. He estado trabajando en iOS 7 con contextos anidados (cola principal hija y padre de cola privada) y NSFetchedResultsControllers y tuve un problema de interbloqueo imposible de arreglar. Cambié a usar MOC independientes y guardar notificaciones y el problema desapareció.

Si alguien necesita una guía rápida para saber cómo cambiar su código a través de, esta página tiene código que está listo para ir (sólo ignorar la recomendación contexto anidado):

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

0

también me dieron un accidente relacionado a developerSubmittedBlockToNSManagedObjectContextPerform.

En mi caso, considere el siguiente patrón llamando al método:

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

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

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

fix: todos los métodos que se van a trabajar en operaciones db se parametrizan con el contexto en el que se van a trabajar, excepto D(), ya que no tiene que trabajar en la operación db, como:

A (contexto) llama B (contexto) B (contexto) llama C (contexto) C (contexto) llama D()

0

I resuelto el mismo problema exacto con los puntos muertos causados ​​por ir a buscar simultánea a partir de dos hilos (BG ejecutado disparó fetchRequest, PRINCIPAL una búsqueda realizada de NSFRC). La solución es crear un nuevo contexto para la operación de sincronización de larga ejecución. No tiene contexto padre, tiene simultaneidad tipo NSPrivateQueueConcurrencyType y está directamente relacionado con un PSC común. Después de que todo el trabajo de ejecución prolongada se realiza dentro de este contexto en segundo plano, lo guardo y lo combino con la pila de contextos en paralelo de reposo utilizando la rutina mergeChangesFromContextDidSaveNotification.

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

Cuestiones relacionadas