Параллелизм с базовыми данными
Существует несколько ситуаций, в которых выполнение операций с Базовыми Данными по фоновому потоку или очереди выгодно, в частности если Вы хотите гарантировать, что пользовательский интерфейс Вашего приложения остается быстро реагирующим, в то время как Базовые Данные выполняют продолжительную задачу. При выполнении параллельных операций с Базовыми Данными, однако, необходимо проявить значительную заботу, что графы объектов не входят в противоречивое состояние.
Если Вы принимаете решение использовать параллелизм с Базовыми Данными, также необходимо рассмотреть среду приложения. По большей части AppKit и UIKit не ориентированы на многопотоковое исполнение; в частности на привязке Какао OS X и контроллерах не ориентированы на многопотоковое исполнение — если Вы используете эти технологии, многопоточность может быть сложной.
Используйте ограничение потока для поддержки параллелизма
Образец, рекомендуемый для параллельного программирования с Базовыми Данными, является ограничением потока: каждый поток должен иметь свой собственный полностью частный контекст управляемого объекта.
Существует два возможных способа принять образец:
Создайте отдельный контекст управляемого объекта для каждого потока и совместно используйте единственного персистентного координатора хранилища.
Это - обычно рекомендуемый подход.
Создайте отдельный контекст управляемого объекта и персистентного координатора хранилища для каждого потока.
Этот подход предусматривает больший параллелизм за счет большей сложности (особенно, если необходимо передать изменения между различными контекстами), и увеличенное использование памяти.
Необходимо создать управляемый контекст на потоке, на котором он будет использоваться. Если Вы используете NSOperation
, отметьте что init
метод вызывается на тот же поток как вызывающая сторона. Вы не должны, поэтому, создавать контекст управляемого объекта для очереди в очереди init
метод, иначе это связано с потоком вызывающей стороны. Вместо этого необходимо создать контекст в main
(для последовательной очереди) или start
(для параллельной очереди).
Используя ограничение потока, Вы не должны передавать управляемые объекты или контексты управляемого объекта между потоками. «Передать» управляемый объект от одного контекста другой через границы потока, Вы также:
Передайте его идентификатор объекта (
objectID
) и используйтеobjectWithID:
илиexistingObjectWithID:error:
на контексте управляемого объекта получения.Соответствующие управляемые объекты, должно быть, были сохранены — Вы не можете передать ID недавно вставленного управляемого объекта к другому контексту.
Выполните выборку на контексте получения.
Они создают локальную версию управляемого объекта в контексте получения.
Можно использовать методы, предоставленные NSFetchRequest
сделать работу с данными через потоки проще и более эффективной. Например, можно сконфигурировать запрос выборки, чтобы возвратить просто идентификаторы объектов, но также и включить данные строки (и обновить кэш строки) — это может быть полезно, если Вы просто собираетесь передать те идентификаторы объектов от фонового потока до другого потока.
Обычно нет никакой потребности использовать блокировки с контекстами управляемого объекта или управляемыми объектами. Однако, если Вы используете единственного персистентного координатора хранилища, совместно использованного многократными контекстами, и хотите выполнить операции на нем (например, если Вы хотите добавить новое хранилище), или если Вы хотите агрегировать много операций в одном контексте вместе, как будто виртуальная единственная транзакция, необходимо заблокировать персистентного координатора хранилища.
Изменения дорожки в других потоках Используя уведомления
Изменения, которые Вы вносите в управляемый объект в одном контексте, не распространены к соответствующему управляемому объекту в различном контексте, если Вы или не повторно выбираете или повторно даете сбой объект. Если необходимо отследить в изменениях потока, внесенных в управляемые объекты в другом потоке, существует два подхода, которые можно проявить, оба уведомления включения. В целях объяснения рассмотрите два потока, и «B», и предположите, что Вы хотите распространить изменения от B до A.
Как правило, на потоке регистр Вас для контекста управляемого объекта сохраняют уведомление, NSManagedObjectContextDidSaveNotification
. При получении уведомления его пользовательский информационный словарь содержит массивы с управляемыми объектами, вставленными, удаленными и обновленными на потоке B. Поскольку управляемые объекты связаны с различным потоком, однако, Вы не должны получать доступ к ним непосредственно. Вместо этого Вы передаете уведомление как параметр mergeChangesFromContextDidSaveNotification:
(который Вы отправляете в контекст на потоке A). Используя этот метод, контекст в состоянии безопасно объединить изменения.
При необходимости в управлении с более прекрасными зернами можно использовать уведомление изменения контекста управляемого объекта, NSManagedObjectContextObjectsDidChangeNotification
— пользовательский информационный словарь уведомления снова содержит массивы с управляемыми объектами, вставленными, удаленными и обновленными. В этом сценарии, однако, Вы регистрируетесь для уведомления на потоке B. При получении уведомления управляемые объекты в пользовательском информационном словаре связаны с тем же потоком, таким образом, можно получить доступ к их идентификаторам объектов. Вы передаете идентификаторы объектов для поточной обработки путем отправки подходящего сообщения в объект на потоке A. На получение на потоке Вы могут повторно выбрать соответствующие управляемые объекты.
Обратите внимание на то, что уведомление изменения представляется NSManagedObjectContext
processPendingChanges
метод. Основной поток связывается в цикл события для приложения так, чтобы processPendingChanges
вызывается автоматически после каждого пользовательского события на контекстах, принадлежавших основному потоку. Дело обстоит не так для фоновых потоков — то, когда метод вызывается, зависит и от платформы и от версии выпуска, таким образом, Вы не должны полагаться на определенную синхронизацию. Если вторичный контекст не находится на основном потоке, необходимо вызвать processPendingChanges
самостоятельно в надлежащих соединениях. (Необходимо установить собственное понятие работы «цикл» для фонового потока — например, после каждого кластера действий.)
Выборка в фоновом режиме для скорости отклика UI
executeFetchRequest:error:
метод внутренне масштабирует свое поведение соответственно для аппаратных средств и рабочей нагрузки. Если необходимо, Базовые Данные создадут дополнительные частные потоки для оптимизации выбирающей производительности. Вы не улучшите абсолютную выбирающую скорость путем создания фоновых потоков в цели. Может все еще быть надлежащим, однако, выбрать в фоновом потоке или очереди, чтобы препятствовать тому, чтобы пользовательский интерфейс Вашего приложения блокировал. Это означает, что, если выборка является сложной или возвращает большой объем данных, можно возвратить управление пользователю и вывести на экран результаты, когда они поступают.
После образца ограничения потока Вы используете два контекста управляемого объекта, связанные с единственным персистентным координатором хранилища. Вы выбираете в одном контексте управляемого объекта на фоновом потоке и передаете идентификаторы объектов выбранных объектов к другому потоку. Во втором потоке (обычно основной поток приложения, так, чтобы можно было тогда вывести на экран результаты), Вы используете второй контекст для сбоя в объектах с теми идентификаторами объектов (Вы используете objectWithID:
инстанцировать объекта). (Этот метод только полезен при использовании хранилища SQLite так как данные от двоичного файла и хранилищ XML сразу считаны в память на открытом.)
Сохранение в Фоновом потоке Подвержено ошибкам
Асинхронные очереди и потоки не препятствуют тому, чтобы приложение вышло. (В частности, все NSThread
- основанные потоки «отсоединяются» — см. документацию для pthread
для полных подробных данных — и процесс работает только, пока все не - не отсоединились, потоки вышли.) При выполнении работы сохранения в фоновом потоке, поэтому, это может быть уничтожено, прежде чем это будет в состоянии завершиться. Если необходимо экономить на фоновом потоке, необходимо записать дополнительный код, таким образом, что основной поток препятствует тому, чтобы приложение вышло, пока вся работа сохранения не завершена.
Если Вы не используете включение потока
Если Вы принимаете решение не использовать образец включения потока — т.е. при попытке передать управляемые объекты или контексты между потоками, и т.д. — необходимо быть чрезвычайно осторожны относительно блокировки, и как следствие Вы, вероятно, инвертируете любое преимущество, которое можно иначе получить из многопоточности. Также необходимо полагать что:
Любое время, которым Вы управляете или управляемые объекты доступа, Вы используете связанный контекст управляемого объекта.
Базовые Данные не представляют ситуацию, где чтения «безопасны», но изменения «опасны» — каждая работа «опасна», потому что каждая работа имеет эффекты когерентности кэш-памяти и может инициировать сбой.
Сами управляемые объекты не ориентированы на многопотоковое исполнение.
Если Вы хотите работать с управляемым объектом через различные потоки, необходимо заблокировать его контекст (см.
NSLocking
).
При совместном использовании контекста управляемого объекта или персистентного координатора хранилища между потоками необходимо гарантировать, что любые вызовы метода сделаны из ориентированного на многопотоковое исполнение объема. Для блокировки необходимо использовать NSLocking
методы на контексте управляемого объекта и персистентном координаторе хранилища вместо того, чтобы реализовать Ваши собственные взаимные исключения. Эти методы помогают предоставить контекстную информацию платформе о намерении приложения — т.е. в дополнение к обеспечению взаимного исключения, они помогают определить объем кластеров операций.
Обычно Вы блокируете контекст или координатора, использующего tryLock
или lock
. Если Вы сделаете это, то платформа гарантирует, что то, что она делает негласно, также ориентировано на многопотоковое исполнение. Например, если Вы создаете один контекст на поток, но все указывающие на того же персистентного координатора хранилища, Базовые Данные заботятся о доступе к координатору ориентированным на многопотоковое исполнение способом ( lock
и unlock
методы NSManagedObjectContext
рекурсия дескриптора).
Если Вы блокируете (или успешно tryLock
) контекст, необходимо сохранить сильную ссылку к тому контексту, пока Вы не вызываете unlock
. Если Вы не делаете в многопоточной среде, можно вызвать мертвую блокировку.