Как мне разрешить этот тупик, который иногда случается?
У меня есть один managedObjectContext с типом параллелизма NSMainQueueConcurrencyType
+ (NSManagedObjectContext *)managedObjectContextMainThread
{
static NSManagedObjectContext *__managedObjectContext=nil;
@synchronized(self)
{
if (__managedObjectContext != nil)
{
}
else {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
}
}
return __managedObjectContext;
}
Основной managedObjectContext НИКОГДА не доступен вне основного потока, за исключением случаев установки другого управляемого ObjectContext.parent. Так что mainManagedObjectContext является родителем для всех потоков.
Теперь, когда я запускаю программу, иногда она заходит в тупик. Я приостанавливаю программу, и вот что я вижу:
Как мы видим на картинке, есть 2 темы, которые, кажется, находятся в тупике. Первым является основной поток.
Это тупик на @synchronize (self). Умеренная.
Другой поток находится в тупике:
Поэтому он блокируется при попытке изменить постоянное хранилище статической переменной, содержащей __managedObjectContext.
Позвольте мне поставить код еще раз, чтобы повторить:
+ (NSManagedObjectContext *)managedObjectContextMainThread
{
static NSManagedObjectContext *__managedObjectContext=nil;
@synchronized(self) //Main thread deadlock here
{
if (__managedObjectContext != nil)
{
}
else {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator]; //Secondary thread dead lock here
}
}
}
return __managedObjectContext;
}
Мой вопрос: почему на земле [__managedObjectContext setPersistentStoreCoordinator:coordinator];
НИЧЕГО еще не получает доступ к __managedObjectContext. Второй поток (не основной) пытается установить __managedObjectContext в качестве родительского контекста. Первый поток просто счастливо ждет в @synchronized. Это ничего не делает.
Так почему тупик и как это решить?
О, дочерний элемент managedObjectContext создан здесь:
@synchronized(self)
{
if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread]; //Stuck here. This goes straight to above function managedObjectContextMainThread where it stucks.
threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[managedObjectContexts setObject:threadContext forKey:[self threadKey]];
}
}
1 ответ
Проблема в том, что я пытался создать основной контекст управляемого объекта в потоке, отличном от основного потока.
По какой-то причине это не работает.
Я все еще люблю ленивую загрузку. Так что все, что мне нужно сделать, это убедиться, что основной managedObjectContext создан на
- основная нить
- перед созданием любых других управляемых объектов.
- Я не хочу гарантировать, что моя программа не пытается сначала получить доступ к другим managedObjectContexts
Так что это похоже на работу для dispatch_sync.
Затем я добавил этот код:
dispatch_sync(dispatch_get_main_queue(),^{
[self managedObjectContextMainThread];//Access it once to make sure it's there
});
до того как я создал весь фоновый дочерний объект managedObjectContexts. Это должно быть быстро, потому что после создания функция будет возвращать только статическую переменную.
+(NSManagedObjectContext *)managedObjectContext {
NSThread *thread = [NSThread currentThread];
//BadgerNewAppDelegate *delegate = [BNUtilitiesQuick appDelegate];
//NSManagedObjectContext *moc = delegate.managedObjectContext;
if ([thread isMainThread]) {
//NSManagedObjectContext *moc = [self managedObjectContextMainThread];
return [self managedObjectContextMainThread];
}
else{
dispatch_sync(dispatch_get_main_queue(),^{
[self managedObjectContextMainThread];//Access it once to make sure it's there
});
}
// a key to cache the context for the given thread
NSMutableDictionary *managedObjectContexts =[self thread].managedObjectContexts;
@synchronized(self)
{
if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread];
//threadContext.persistentStoreCoordinator= [self persistentStoreCoordinator]; //moc.persistentStoreCoordinator;// [moc persistentStoreCoordinator];
threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[managedObjectContexts setObject:threadContext forKey:[self threadKey]];
}
}
return [managedObjectContexts objectForKey:[self threadKey]];
}