Как мне разрешить этот тупик, который иногда случается?

У меня есть один 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 создан на

  1. основная нить
  2. перед созданием любых других управляемых объектов.
  3. Я не хочу гарантировать, что моя программа не пытается сначала получить доступ к другим 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]];
}
Другие вопросы по тегам