Неожиданное нарушение многопоточности данных
Я использую Apple concurrency отладчик основных данных.
-com.apple.CoreData.ConcurrencyDebug 1
Время от времени я получил __Multithreading_Violation_AllThatIsLeftToUsIsHonor__
, даже я почти уверен, что нить не нарушена.
Это часть кода, в котором происходит исключение (код является частью протокола, который расширяет NSManagedObject):
public static func find(arrayBy predicate: NSPredicate, sort: [NSSortDescriptor] = [], limit: Int? = nil) -> [Self] {
let fetchRequest = NSFetchRequest<Self>(entityName: "\(Self.self)")
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = sort
do {
return try Context.current.fetch(fetchRequest) // Exception!!!
} catch let error {
Logger.fatal("Failed to perform fetch: \(error)")
return []
}
}
Код выполняется в контексте блока perform:
.
Вот информация о потоках:
![введите описание изображения здесь]()
и информацию об отладчике, чтобы подтвердить, что выполнение выполняется в правом NSManagedContext:
(lldb) po Context.current
<StoreContext: 0x7f854b556610>
Имя объекта успешно извлечено:
po fetchRequest.entityName!
"Position"
Предикат построен из чистых объектов String (никаких управляемых объектов вообще не используется):
(lldb) po fetchRequest.predicate!
ANY employees.company.id == "282372"
В этом случае дескрипторы сортировки вообще не используются:
po fetchRequest.sortDescriptors!
0 elements
Предел полностью игнорируется.
Что мне не хватает? Кто-нибудь знает, что здесь может быть не так?
Edit:
Чтобы уточнить, Context.current
устанавливается непосредственно перед отправкой блока:
Context.current = managedObjectContext
managedObjectContext.performAndWait {
//...
}
Вы можете увидеть на скриншоте, что Thread 13
работает на Queue: NSManagedObject 0x7f854b556610 (serial)
. Кроме того, когда возникает исключение Context.current
возвращает <StoreContext: 0x7f854b556610>
. Если посмотреть на адрес памяти, то он легко выполнит в правой очереди.
Ответы
Ответ 1
Сохранение "текущего" фонового контекста в глобальном состоянии является плохой практикой. Я не могу указать, где именно в вашем коде запутался, но неожиданное может случиться с глобальным состоянием, когда задействовано многопоточность. Измените функцию find
, чтобы принять параметр context
в качестве параметра. Это позволит избежать использования какого-либо глобального состояния и, скорее всего, устранит вашу проблему.
Ответ 2
Рекомендуется избегать использования API .performAndWait
, который будет использоваться только в самых редких случаях, когда все остальное не удалось!
Оцените морфинг Context.current
до managedObjectContext.perform
во всем приложении.
Результатом этого изменения будет добавление асинхронности во всех операциях с вашей базой данных.
Это может показаться массовым изменением, чтобы спросить, но поверьте мне, просто решите рассматривать Core Data как полностью асинхронный API, и жизнь будет намного лучше.
Я уверен, что текущая авария, с которой вы сталкиваетесь, является результатом сложного результата поврежденного поведения .performAndWait
Это, this и это, некоторые хорошие чтения по теме.
Ответ 3
Проверьте этот блог, чтобы лучше понять многопоточность в Core Datap >
https://cocoacasts.com/core-data-and-concurrency/
https://cocoacasts.com/more-core-data-and-concurrency/