Подтвердить что ты не робот

Основные данные вложенные контексты управляемых объектов и частые взаимоблокировки/зависания

У меня проблема, которая почти идентична проблеме, описанной этим человеком здесь, но на нее нет ответа:

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

Вот проблема:

У меня есть родительская настройка MOC с NSPrivateQueueConcurrencyType и набор постоянных хранителей хранилища, у нее есть настройка MOC для детей с NSMainQueueConcurrencyType. Идея, заключающаяся в большой длительной работе и экономии, может быть выполнена на частном MOC, освобождающем основной поток от блокировки пользовательского интерфейса. К сожалению, я, похоже, сталкиваюсь с несколькими ситуациями, которые вызывают взаимоблокировки.

Если дочерний MOC (в основном потоке) выполняет выборку с помощью NSFetchedResultsController, родительский контекст отправляется -executeFetchRequest: он может создать тупик. Обе операции выполняются в контексте executeBlock: для соответствующих MOC, хотя документы, похоже, указывают на то, что использование основного потока concurrency типа MOC в основном потоке без executeBlock: отлично.

Похоже, что частная очередь ждет блокировки PSC, которая уже заблокировала дочерний контекст в основном потоке. Похоже, что дочерний контекст (удерживая блокировку PSC) пытается dispatch_sync в родительский контекст, и поэтому они оба ждут друг друга.

Является ли PriveQueue → MainQueue поддерживаемой конфигурацией? Кажется, что большинство людей по-прежнему имеют родительский контекст в основном потоке.

Основной поток выглядит следующим образом:

> #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: когда происходят эти взаимоблокировки. Большую часть времени он будет застревать, пытаясь вызвать ошибку в объекте в результате запроса имени раздела. В качестве теста я попытался воспроизвести, что делает FRC и выполнил executeFetchRequest, а затем повторил результаты, прося каждый объект для этого имени раздела. И это не вызывает тупика. Если я покину FRC, чтобы выполнить performFetch: после того, как я сделаю свой тест, он все равно закроется. Я на 99% уверен, что FRC имеет проблему синхронизации с вложенными контекстами.

Вопрос: Кто-нибудь знает, почему эта проблема возникает? Вы знаете, как его решить? Это ошибка?

4b9b3361

Ответ 1

Я просто прочитал эту публикацию SO, где fabrice truillot de chambrier рекомендует не использовать вложенные контексты в настоящее время. Он дает ссылку на статью Основные данные растущих болей.

Из этой статьи:

NSFetchedResultsController deadlocks

Вы никогда не хотите, чтобы ваше приложение зашло в тупик. С NSFetchedResultsController и вложенные контексты, это довольно легко делать. Используя ту же самую настройку UIManagedDocument, описанную выше, выполнение выборки запроса в контексте частной очереди при использовании NSFetchedResultsController с контекстом основной очереди тупиковый. Если вы начинаете оба примерно в одно и то же время, это происходит с почти 100% консистенции. NSFetchedResultsController, вероятно, приобретая блокировку, которой она не должна быть. Об этом сообщается как фиксированном для предстоящего выпуска iOS.

радар://11861499 исправлен в предстоящем выпуске

Это, похоже, точно описывает вашу проблему.

Ответ 2

Для моего приложения iOS 6 у меня есть такая же concurrency настройка, что и OP - родительский MOC с использованием частной очереди и дочернего MOC в основном потоке. У меня также есть NSFetchedResultsController, который использует дочерний MOC для обновления UITableViewController. Оба эти MOC инициализируются в AppDelegate и должны использоваться в приложении. AppDelegate имеет два метода: savePrivateThreadMOCToCoreData и saveMainThreadMOCToCoreData, чтобы сохранить изменения на компакт-диске. При запуске я отправляю инициализатор coredata в частной очереди следующим образом. Идея состоит в том, чтобы немедленно отбросить пользователя в представление таблицы и позволить инициализатору обновлять основные данные в фоновом режиме.

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

Изначально, когда savePrivateThreadMOCToCoreData делал сбережения в -performBlock, я видел те же самые взаимоблокировки psynch_mutex, описанные в "" Основные растущие боли" выше. Я также видел сбои, если я пытался читать данные в TableVC во время сохранения.

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

Чтобы преодолеть это, я переключился на выполнение с использованием -performBlockAndWait. Я перестала видеть тупики и сбои, но не чувствовала, что пользовательский интерфейс ждет сохранения. Наконец, я удалил все вызовы на -performBlock * и использовал простой ваниль [privateMOC save: & ошибка], и так вот, все мои проблемы исчезли. Выбранный контроллер результатов считывает частично сохраненные данные и обновляет таблицу, больше не блокирует или "мутирует, будучи перечисляемым".

Я подозреваю, что -performBlock * предполагается использовать другими потоками, которые не создают соответствующий MOC, для запроса на него операций. Поскольку мои личные и основные потоки MOC принадлежат делегату приложения, сохранение частного MOC не должно использовать -performBlock *.

Вероятно, это связано с тем, что хотя моя среда сборки - это iOS 6, мой целевой целевой пакет SDK iOS 5.0 для развертывания базы данных. Похоже, что другие не видят эту проблему с iOS 6.

Ответ 3

Это происходит со мной, потому что родители настроены с помощью NSMainQueueConcurencyType

Чтобы решить эту проблему, я создаю управляемый объект для mainQueue как дочерний. Я называл reset каждый раз, когда я хочу загружать файлы, чтобы гарантировать, что данные в mainQueue одинаковы с родителем. Часто это не так.

Ответ 4

У меня тоже произошел сбой, связанный с 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-операцией, например:

A (контекст) вызывает B (контекст) B (контекст) вызывает C (контекст) C (контекст) вызывает D()

Ответ 5

Я решил ту же проблему с тупиками, вызванную одновременной выборкой из двух потоков (BG выполнил fetchRequest, MAIN выполнил выборку NSFRC). Решение заключается в создании нового контекста для продолжительной синхронизации. Он не имеет родительского контекста, он имеет concurrency type NSPrivateQueueConcurrencyType и напрямую связан с общим PSC. После того, как в этом контексте выполняется полная работа в фоновом режиме, я сохраняю его и объединяя его с остальным параллельным стеком контекстов с помощью mergeChangesFromContextDidSaveNotification.

Отличное решение реализовано в Magical Record 3. Подробнее читайте здесь: fooobar.com/questions/186885/....

Ответ 6

Идея, заключающаяся в большой длительной работе и экономии, может быть выполнена на частной MOC

Как вы реализуете эту идею? Вы используете что-то вроде этого:

- (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 для загрузки загружаемого материала и манипуляции с основными данными в дочернем контексте (в главной очереди). Он работает нормально. Таким образом, родительский контекст кажется бесполезным.. Но по крайней мере он не вызывает тупика.

- (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 safe thread (main thread)
        [document.managedObjectContext performBlock:^{ 
            // execute fetch request on parent context
            // save document  
        }];
    });
    dispatch_release(fetchQ);
}

Ответ 7

Я просто хотел перезвонить и полностью согласиться с тем, чтобы избежать вложенных контекстов. Я работаю в iOS 7 с вложенными контекстами (основной дочерней и средней очереди очереди) и NSFetchedResultsControllers, и было невозможно устранить проблему взаимоблокировки. Я переключился на использование независимых MOC и сохранил уведомления, и проблема исчезла.

Если кому-то нужно краткое руководство по изменению кода, на этой странице есть готовый код (просто игнорируйте рекомендации вложенных контекстов):

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