核心数据 - 多线程 - 启动时的竞争条件
Posted
技术标签:
【中文标题】核心数据 - 多线程 - 启动时的竞争条件【英文标题】:Core Data - multi thread - race condition on startup 【发布时间】:2012-01-26 10:29:35 【问题描述】:我有一个使用 Core Data 的多线程应用程序。我在启动时看到了很多崩溃,以及各种奇怪的错误消息。但是,有时它可以正常工作!我从未见过我自己的 iPhone4 崩溃,但它确实在其他设备上崩溃。我想我已经解决了这个问题,但不知道如何最好地解决它。
当应用程序启动时,我使用 GCD 从 Web 下载数据并在后台线程上更新核心数据。在主线程上,我继续设置表视图,其中一个命令触发NSManagedObjectContext
getter。我使用的几乎都是标准模板代码,所以这是第一次运行,通常的惰性实例化代码会创建NSPersistentStoreCoordinator
。
但是后台线程正在创建一个新的NSManagedObjectContext
供自己使用。这包括获取NSPersistentStoreCoordinator
,如下所示:
// Create a new NSManagedObjectContext for this thread
NSManagedObjectContext *threadContext = nil;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
threadContext = [[NSManagedObjectContext alloc] init];
[threadContext setPersistentStoreCoordinator:coordinator];
NSMergePolicy *mergePolicy = [[NSMergePolicy alloc] initWithMergeType:NSMergeByPropertyObjectTrumpMergePolicyType];
[threadContext setMergePolicy:mergePolicy];
else
NSLog(@"Error - No NSPersistentStoreCoordinator");
我 90% 确定问题是两个线程同时访问NSPersistentStoreCoordinator
代码。两个线程都认为对象是nil
,因此创建对象。这会导致第一个线程到达那里的问题,因为 ivar 突然指向错误的位置。到那时坏事就会发生!
那么如何最好地解决这个问题?我通过在后台线程中添加sleep(1)
暂时解决了这个问题:) 但我不确定这真的是最好的解决方案!我尝试更改 NSPersistentStoreCoordinator
属性,使它们是原子的,但这并没有帮助。我应该将每个标准吸气剂包装在@synchronise
中以互斥锁吗?我还没有时间尝试这个,但它应该会阻止两个线程运行相同的代码。还是有更好的办法?
想法?
谢谢克雷格
========================================
有关信息,以下是我看到的一些错误和崩溃。这些是非常随机的,但它可能会帮助其他人在未来找到这篇文章。
2012-01-18 22:19:57.586 CBF[10468:11d03]-[NSSQLModel _addPersistentStore:identifier:]: 无法识别的选择器发送到实例 0x6b80390
2012-01-18 22:19:57.595 CBF[10468:11d03] * 终止应用程序由于 未捕获的异常'NSInvalidArgumentException',原因:'-[NSSQLModel _addPersistentStore:identifier:]: 无法识别的选择器发送到实例 0x6b80390'
2012-01-19 16:58:06.671 CBF[11738:fe03] -[__NSCFDictionary _hasPrecomputedKeyOrder]:无法识别的选择器发送到实例 0x6d55040
2012-01-25 21:45:31.174 CBF[16911:1310b]-[__NSArrayM _addPersistentStore:identifier:]: 无法识别的选择器发送到实例 0x6d59370
2012-01-25 21:45:31.175 CBF[16911:1310b] * 终止应用程序由于 未捕获的异常 'NSInvalidArgumentException',原因:'-[__NSArrayM _addPersistentStore:identifier:]: 无法识别的选择器发送到实例 0x6d59370'
【问题讨论】:
【参考方案1】:为什么不直接删除惰性实例化,而是在应用启动时立即创建协调器?
【讨论】:
谢谢保罗。你看,现在你这么说太明显了!实际上,我也可以保留惰性实例化,但只需确保我专门调用 NSManagedObjectContext getter 来启动整个设置过程并将结果丢弃。如果我在任何其他核心数据之前这样做,那么一切都会好起来的。非常感谢。 嗨,克雷格,我不明白解决方案,您能否更详细地解释一下,因为在同步核心数据时遇到类似的问题,并且用户尝试查看被删除的特定记录 @iphonegeek 是2个线程同时访问核心数据栈造成的。这通常没问题,但是第一次使用它时,通常会出现延迟实例化代码,上面写着if blah=nil then do alloc/init
问题是第二个线程到达了该方法并且 ivars 仍然为零,因为分配/初始化是由第一个线程尚未完成。解决方案是在分叉第二个线程之前在主线程中添加以下行:NSManagedObjectContext *unused __attribute__((unused)) = [self managedObjectContext];
这样可以确保创建所有内容。以上是关于核心数据 - 多线程 - 启动时的竞争条件的主要内容,如果未能解决你的问题,请参考以下文章