Helpers - компьютеры, интернет, программирование

Сохранение контекста управляемого объекта создает взаимоблокировку в PerformBlock в iOS 5.

Я долго искал решение этой проблемы и пока не нашел его.

Я разрабатываю приложение iOS с основными данными. Я создал два контекста управляемых объектов (MOC), которые указывают на один и тот же координатор постоянного хранилища. Один MOC (называемый self.moc) инициируется с параллелизмом основной очереди, тогда как другой mov (называемый self.bmoc) инициируется с параллелизмом частной очереди. Я убедился, что self.moc работает только в основном потоке, а self.bmoc работает только в пределах своего блока performBlock или performBlockAndWait.

Однако я столкнулся с этой странной ситуацией, когда мое приложение зависает на строке [self.bmoc save:nil]. Поскольку действие сохранения выполняется в блоке performBlock, я не вижу причин, по которым оно может зайти в тупик. Поскольку он зависает на этой строке, я не могу получить сообщение об ошибке, даже если использую [self.bmoc save:&error], а не nil.

Ниже приведен код, который воспроизведет проблему. Хотя у меня есть много функций, похожих на приведенную ниже, только эта создает проблему. Я не могу понять причину проблемы, и любое понимание очень ценится. Благодарю вас!

-(void)createEmptyUserData {
    [self.bmoc performBlock:^{
        User* user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.bmoc];
        /* sets user object */
        [self.bmoc save:nil];
    }];
}

Примечание. Этот фрагмент кода выполняется в основном потоке.


  • Любое понимание, где возникает тупик? Любой метод validateSomething или метод willSave что-то блокирует? Тупик означает, что 2 потока ждут друг друга, где ждет другой поток? 13.07.2012
  • Спасибо за ваш ответ. После того, как этот фрагмент кода выполнен, основной поток продолжается и заканчивается в [self.bmoc performBlockAndWait^{}], где он ожидает завершения выполнения кода выше, что приводит к зависанию основного потока. Я не знаю о каких-либо методах validateSomething или willSave. 13.07.2012
  • Вы можете попробовать добавить -com.apple.CoreData.SQLDebug 3, чтобы включить отладку и посмотреть, действительно ли что-то сохраняется. 13.07.2012
  • Также, возможно, этот блок на самом деле ожидает завершения предыдущего блока, и это тот, который застрял? 13.07.2012
  • Являются ли контексты вложенными или они просто используют один и тот же постоянный координатор t store? 13.07.2012
  • Что вы подразумеваете под параллелизмом частной очереди? Если вы имеете в виду, что он работает параллельно в своей собственной очереди GCD или NSOperationQueue, у вас могут возникнуть проблемы, поскольку вы не можете гарантировать, в каком потоке выполняется блок операции/GCD. 13.07.2012
  • @ Рори О'Брайан Мне кажется, что данные действительно были сохранены. Поскольку я окружаю этот блок performBlock, если он ожидает завершения какого-то другого блока, не должен ли он ждать в начале performBlock, а не save? 14.07.2012
  • @svena можете ли вы объяснить, что вы подразумеваете под вложенными контекстами? Для меня у них один и тот же постоянный координатор магазина. Я сделал [moc setPersistentStoreCoordinator:self.psc] для обоих MOC. 14.07.2012
  • @JeremyP Я имел в виду, что я инициализировал self.bmoc с помощью [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];. Это новое в iOS 5, и из того, что я узнал, пока я выполняю действия, связанные с coredata в performBlock, у меня не должно быть проблем с потоками. Но видимо эта проблема доказала, что это не тот случай. Спасибо за вашу помощь! 14.07.2012
  • Под вложенными контекстами я имел в виду шаблон, в котором у вас будет корневой контекст, отвечающий за постоянное хранилище, а другой контекст будет дочерним контекстом корневого контекста. Я даже упомянул об этом, потому что в WWDC2012 Session 214 — Core Data Best Practices инженеры Apple прямо предупредили НЕ использовать -performBlockAndWait: в дочернем контексте из блока корневого контекста, поскольку это может легко вызвать взаимоблокировку. 14.07.2012
  • У меня возникла тупиковая ситуация с использованием вложенных контекстов в UIManagedDocument, которая оказалась связана с сохранением UIManagedDocument (а не контекста) из частного фонового потока вместо основного потока. Согласно предложению Джоди, это было довольно легко обнаружить в отладчике, просматривая различные потоки в точке тупика в Xcode. Определенно хорошее место для расследования. 14.07.2012
  • @свена я вижу. Хотя нет, у меня нет вложенных контекстов, но у меня действительно есть проблемы того же типа (и я понял свою проблему: см. комментарий под ответом Джоди). У меня еще не было возможности посмотреть сессию 214 конференции WWDC2012 — Рекомендации по основным данным, но я сделаю это в ближайшее время, прежде чем задавать подобные вопросы. Благодарю вас! 15.07.2012
  • @ Рори О'Брайан Я новичок в Objective-C (хотя у меня есть опыт работы с C/C++) и xcode. И, честно говоря, я не считаю интерфейс отладки xcode таким уж полезным. Однако, возможно, я не знаю, как им правильно пользоваться, но мне кажется, что я должен научиться им пользоваться, так как он может, как вы говорите, предоставить полезную информацию. Спасибо всем еще раз. 15.07.2012

Ответы:


1

Есть две основные причины, по которым вы «зависаете» в такой ситуации.

  1. У вас есть вложенный вызов для выполненияBlockAndWait или какой-либо другой вызов синхронного потока/очереди.

  2. Один из ваших блоков не возвращается и работает вечно.

И то, и другое легко увидеть, посмотрев на стеки каждого запущенного потока во время «зависания».

PerformBlock просто берет исполнительный блок и добавляет его в очередь, а затем немедленно возвращается. Затем какой-то другой поток извлекает блоки выполнения из очереди и выполняет их.

PerformBlockAndWait выполняется в контексте вызывающего потока. По сути, он ожидает выполнения текущих блоков выполнения, находящихся в очереди, а затем запускает запрошенный код в текущем потоке.

Он не возвращается, пока вызов не будет завершен.

Итак, я готов поспорить, что у вас либо есть несколько вложенных вызовов для PerformBlockAndWait, либо один из ваших асинхронных блоков выполнения не завершается.

Посмотрите на стек во время зависания...

Кроме того, запишите выполнение своего блока, чтобы вы могли видеть, когда каждый блок запускается и выходит.

13.07.2012
  • Это похоже на то, что сказал Фабрис Трюйо де Шамбрие. Я углублюсь в проблему в указанном вами направлении и вернусь к вам позже. Спасибо. 14.07.2012
  • Спасибо, что направили меня к ответу. Я отредактирую это позже и предоставлю некоторые подробности о причине проблемы и о том, как я ее решил. 15.07.2012
  • Я думал, что могу редактировать комментарии, но, видимо, я ошибался. В любом случае, причина проблемы в том, что [self.bmoc save:nil]; вызывает contextDidChange: после завершения процесса сохранения. В моем contextDidChange: у меня было [self.moc performBlockAndWait^{ //mergeContext }]. Как я уже упоминал в комментарии под своим вопросом, мой основной поток продолжается и заканчивается на [self.bmoc performBlockAndWait^{}]. Проблема должна стать ясной: обе очереди ждут завершения друг друга, что приводит к взаимоблокировке. Мое решение состоит в том, чтобы изменить performBlockAndWait в contextDidChange: на lock и unlock. 15.07.2012
  • Является ли contextDidChange: вашим обработчиком уведомлений? Я бы предпочел вызвать [self.moc PerformBlock^{ /* что угодно */ }]; над явной блокировкой. 15.07.2012
  • Да. Поскольку [self.moc performBlock^{ /* whatever */ }] вызывает взаимоблокировку, я полагаю, что должен использовать явную блокировку. 16.07.2012
  • Нет... PerformBlock просто помещает блок в очередь и немедленно возвращается. PerformBlockAndWait является причиной взаимоблокировки. 16.07.2012
  • Поскольку мне нужно объединять контексты после каждого [moc save:], я выбираю performBlockAndWait:, а не performBlock. Причина этого, например, в том, что после того, как мой self.bmoc сохранил полученные данные из Интернета, мой self.moc должен немедленно получить доступ к данным и обновить пользовательский интерфейс. 17.07.2012
  • Новые материалы

    Интуитивное понимание тензоров в машинном обучении
    Тензор является важной концепцией во многих научных областях, таких как математика, физика, обработка сигналов и компьютерное зрение, и это лишь некоторые из них. В математике тензор — это..

    Использование машинного обучения для диагностики болезни Альцгеймера, часть 4
    Маркеры семантической согласованности для ранней диагностики болезни Альцгеймера (arXiv) Автор: Давиде Колла , Маттео Дельсанто , Марко Агосто , Бенедетто Витиелло , Даниэле Паоло Радичони..

    Почему объяснимость так важна прямо сейчас?
    По мере того, как системы искусственного интеллекта и инструменты на основе машинного обучения распространяются в нашей повседневной жизни, как практики, так и критики все чаще заявляют о..

    Анимированный математический анализ
    Использование Manim для создания математических анимированных визуализаций Визуализация данных помогает понять скрытые закономерности в данных, которые невозможно визуализировать..

    Создание простого слайдера изображений с помощью JavaScript
    Узнайте, как создать базовый слайдер изображений с помощью HTML, CSS и JavaScript. Введение В этом уроке мы создадим удобный слайдер изображений, используя JavaScript, HTML и CSS. Ползунок..

    Создание базы данных с помощью супергероя «Python»
    В этом посте мы узнаем, как создать «базу данных SQLite с помощью модуля python sqlite3, создав простую функцию входа и регистрации. Готовы ли вы к этому путешествию? Если да , давайте приступим..

    ИИ для чайников: руководство для начинающих по пониманию будущего технологий
    Вы чувствуете, что остались позади в мире ИИ? Не волнуйтесь, вы не одиноки! Со всей этой шумихой вокруг искусственного интеллекта может быть трудно понять, с чего начать. Но не позволяйте сленгу..