【问题标题】:How do I resolve this deadlock that happen ocassionally?如何解决偶尔发生的这种死锁?
【发布时间】:2012-10-11 09:24:05
【问题描述】:

我有一个并发类型为 NSMainQueueConcurrencyType 的 managedObjectContext

+ (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 永远不会在主线程之外访问,除非设置另一个 managedObjectContext.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]];
    }
}

【问题讨论】:

    标签: objective-c ios5 core-data nsmanagedobjectcontext xcode4.5


    【解决方案1】:

    问题是我试图在不同于主线程的线程上创建主托管对象上下文。

    由于某种原因它不起作用。

    我仍然喜欢延迟加载。所以我需要做的就是确保主 managedObjectContext 是在

    1. 主线程
    2. 在创建任何其他 managedObjectContexts 之前。
    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]];
    }
    

    【讨论】:

    • 只是一个简单的说明:创建托管对象上下文是一个非常便宜的操作。真的是!因此,实际上并不需要懒惰地创建上下文,只会增加额外的复杂性,编写更多代码,而您或多或少什么也得不到。延迟创建/加载应该只应用在真正有意义的地方。如果您懒惰地创建上下文,为什么不将其他所有内容也一样?字符串、数字、小数组都可以懒惰地创建,但这并不是为了保持简单。
    猜你喜欢
    • 1970-01-01
    • 2018-10-22
    • 2021-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多