多线程环境中的 PersistentStore 协调器导致“未找到存储”崩溃

Posted

技术标签:

【中文标题】多线程环境中的 PersistentStore 协调器导致“未找到存储”崩溃【英文标题】:PersistentStore Coordinator in a multithreaded environment cause "No Store Found" Crash 【发布时间】:2014-05-08 07:06:58 【问题描述】:

Managedobjectcontext save:NSOperation 子类的查询会导致此异常。我不确定这里出了什么问题,但似乎是线程锁定问题。谁能帮我解决这个问题?

一个 PSC 被多个 NSOperation 子类使用,每个子类都有自己的 MOC。也许我需要将@synchronized 放在某个地方以保证线程安全?

Fatal Exception: NSInternalInconsistencyException

This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.

Thread : Fatal Exception: NSInternalInconsistencyException

0  CoreFoundation                 0x3018af0b __exceptionPreprocess + 130
1  libobjc.A.dylib                0x3a921ce7 objc_exception_throw + 38
2  CoreData                       0x2fedd689 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 3228
3  CoreData                       0x2fefef49 -[NSManagedObjectContext save:] + 824
4  ChevronRetail                  0x0006aae5 -[MyParseOperation myMethod:] (MyParseOperation.m:159)
5  ChevronRetail                  0x0006adbf -[MyParseOperation main] (StationFinderParseOperation.m:192)
6  Foundation                     0x30abe875 -[__NSOperationInternal _start:] + 772
7  Foundation                     0x30b62745 __NSOQSchedule_f + 60

附加信息: PersistentStoreCoordinator 保存在多个控制器中的 MOC 访问的 appdelegate 中

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator   
    if (_persistentStoreCoordinator != nil) 
        return _persistentStoreCoordinator;
            
    NSString *documentsDirectoryPath = [self applicationDocumentsDirectory];

    NSString *storePath = [documentsDirectoryPath stringByAppendingPathComponent:@"MyDatabase.sqlite"];
    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];   
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                         [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                         [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

    NSError *error;

    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error])
    

    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    //abort();
    
    return _persistentStoreCoordinator;



- (NSManagedObjectContext *)managedObjectContext 

    if (_managedObjectContext != nil) 
        return _managedObjectContext;
    

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

    if (coordinator != nil) 
        _managedObjectContext = [NSManagedObjectContext new];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    
    [_managedObjectContext setMergePolicy:NSOverwriteMergePolicy];
    return _managedObjectContext;

【问题讨论】:

添加你的 MOC 和 PSC 初始化代码 现在没有时间形成答案,但您在初始化 PSC 时遇到了竞争条件 【参考方案1】:

Apple 样板 PSC 初始化代码不是线程安全的(MOC 初始化代码也不是)。HERE 你可以找到类似的问题。

在多线程环境中,2 个(或更多)线程可以调用 PSC 初始化代码。 一种解决方法是在您的“应用程序确实完成启动...”中添加这些行:

[self persistentStoreCoordinator];
[self managedObjectContext];

之前发出ANY使用CoreData堆栈的操作/流程。 这确保只有一个线程(主线程)在初始化堆栈,但这否定了当前实现试图实现的“延迟加载”原则。

错误原因是这种执行流程:

线程 1 测试是否有 _persistentStoreCoordinator 并发现它为零。 线程 2 测试是否有 _persistentStoreCoordinator 并发现它为零。 线程 1 和线程 2 继续进入初始化代码部分。 线程 1 到达线路(但尚未返回):

return _persistentStoreCoordinator;

线程 2 执行行:

_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

线程 1 返回一个没有存储的协调器,并在操作中使用它来初始化私有 MOC,MOC 保存并崩溃。

为了保持“延迟加载”的梦想,您可以以这种方式实现初始化代码:

//UNTESTED
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator

    if (_persistentStoreCoordinator != nil) //someone already initilized this part of the stack
        return _persistentStoreCoordinator;//no need to lock
    
    @synchronized(self) 
        if (_persistentStoreCoordinator == nil) //check under lock
            _persistentStoreCoordinator = [self __persistentStoreCoordinator];//only one thread could set the ivar
        
    
    return _persistentStoreCoordinator;



- (NSPersistentStoreCoordinator*) __persistentStoreCoordinator

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MyDatabase.sqlite"];
    NSError *error = nil;
    NSPersistentStoreCoordinator* coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType
                                   configuration:nil
                                             URL:storeURL
                                         options:options
                                           error:&error]) 
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    
    return coordinator;//always return a full fledged coordinator

您可以使用任何您喜欢的同步机制来防止这种竞争条件,这只是一个简单的示例。

这种竞争条件也适用于managedObjectModelmanagedObjectContext。 解决方法类似。

【讨论】:

谢谢,这很有帮助 - 目前每个控制器都有自己的 MOC,所以我认为不应该存在竞争条件。有没有办法在将更新推送到商店之前测试此代码? -- 问题是我在个人测试期间从未见过崩溃,但在商店我看到很多崩溃。 在访问协调器期间暂停线程并尝试重现您认为会导致错误的竞争条件。

以上是关于多线程环境中的 PersistentStore 协调器导致“未找到存储”崩溃的主要内容,如果未能解决你的问题,请参考以下文章

多线程配合协程

python——进程线程协程

python进阶之多线程(简单介绍协程)

Python来实现并发的Web Server,其中采用了多进程多线程协程单进程单线程非阻塞的方式

单线程并发——协程

python多线程多进程协程的使用