How do I resolve this deadlock that happen ocassio

2019-04-16 02:11发布

I have one managedObjectContext with concurency type of 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;
}

The main managedObjectContext is NEVER accessed outside of the main thread except when setting the other managedObjectContext.parent. So that mainManagedObjectContext is the parent for all threads.

Now when I run the program, sometimes it reach a deadlock. I pause the program and this is what I see:

enter image description here

As we see in the picture, there are 2 threads that seems to be in the deadlock. The first is the main thread.

It deadlock on @synchronize (self). Reasonable.

The other thread is deadlock on:

enter image description here

So it locks when trying to change the persistent store of the static variable that hold the __managedObjectContext.

Let me put the code again to reiterate:

+ (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;
}

My question is why in the earth the [__managedObjectContext setPersistentStoreCoordinator:coordinator];

NOTHING else is accessing __managedObjectContext. The second thread (the non main one) is trying to set the __managedObjectContext as the parent context. The first thread is just waiting happily in @synchronized. It doesn't do anything.

So why the deadlock and how to solve that?

Oh the child managedObjectContext is created here:

@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条回答
贪生不怕死
2楼-- · 2019-04-16 02:59

The problem is I tried to create the main managed object context on a thread different than the main thread.

For some reason it doesn't work.

I still love lazy loading. So all I need to do is to ensure that the main managedObjectContext is created on

  1. the main thread
  2. before any other managedObjectContexts are created.
  3. I do not want to ensure that my program do not try to access the other managedObjectContexts first

So that looks like a job for dispatch_sync.

I then added this code:

    dispatch_sync(dispatch_get_main_queue(),^{
        [self managedObjectContextMainThread];//Access it once to make sure it's there
    });

before I created all background child managedObjectContexts. That should be fast because once created the function will only return the static variable.

+(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]];
}
查看更多
登录 后发表回答