【发布时间】:2012-09-06 18:27:56
【问题描述】:
总结:当我在多线程场景中调用[myMOC mergeChangesFromContextDidSaveNotification:notification] 时,我的应用程序挂起。
详细情况是这样的:
我的应用程序在首次启动时从服务器下载大量数据并将其存储在 Core Data 中。它分为几个部分。解析整个事情需要几秒钟,大部分时间都花在了其中的两个块上。所以为了加快速度,我已经并行化了这两个块。这是它的样子:
NSArray *arr = [self parseJsonData:downloadedNSData]; //turns NSData into JSON array
//using NSJSONSerialization
NSMutableArray __block *first = [[NSMutableArray alloc]init];
NSMutableArray __block *second = [[NSMutableArray alloc]init];
//put half of arr in first and half in second with a loop
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dipatch_group_create();
dispatch_group_async(group, queue, ^
{
for (id element in first)
{
[MyDataClass parseData:element]; //create NSManagedObject subclass, save
}
[self saveContext];
});
dispatch_group_async(group, queue, ^
{
for (id element in second)
{
[MyDataClass parseData:element]; //create NSManagedObject subclass, save
}
[self saveContext];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
[self saveContext] 在 MOC 上调用 save。这当然是在多个线程上完成的,所以我需要为每个线程创建一个单独的ManagedObjectContext。这是通过通过此对象 (self) 上的属性公开 MOC 来实现的,该属性维护线程名称的NSMutableDictionary(在NSThread 上调用description)到NSManagedObjectContexts。访问时,如果没有[NSThread currentThread] 的 MOC,它会创建一个新的 MOC,将其添加到字典中并存储它。创建每个 MOC 时,我都会订阅其更改通知:
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification object:createdMOC];
在mergeChanges 中,我循环浏览我的上下文字典,并在所有上下文上调用mergeChangesFromContextDidSaveNotification,但发生这种情况的线程除外。更具体地说,我使用[performSelector:onThread:withObject:waitUntilDone:] 让每个 MOC 在为其创建的线程上执行此操作。
我还使用NSLock 围绕[myMOC save] 和我的mergeChanges 方法进行锁定。如果我没有锁定这些,我会收到“Cocoa error 133020”,apparently 表示合并更改时出错。
所以,这就是我的日志记录告诉我正在发生的事情:
- 线程 1 获取要保存的锁并开始保存
- 线程 1 上出现合并上下文通知。线程 1 获取用于合并更改的锁并开始该过程。它为主线程合并 MOC 的更改,然后在为后台线程执行 MOC 之一时挂起。
- 线程 2 开始保存但从未获取保存锁,因为另一个线程在尝试合并更改时卡住了。
那么,为什么在合并更改时它会挂起?有没有更好的方法来处理这种情况?
更新:我尝试在我的 [MOC save] 调用周围使用 [persistentStoreCoordinator lock] 而不是仅使用 NSLock 进行锁定,但无济于事。我还尝试在调用 [self saveContext] 之前在 dispatch_group_async 调用之一中添加 [NSThread sleepForTimeInterval:2],但没有帮助。
更新 2: 也许更好的问题是为什么我会遇到合并冲突(Cocoa 错误 133020)。这是预期的吗?我是否正确地进行了合并(合并到除了一个保存之外的所有上下文)?
更新 3:我已发布 another question 以解决我如何进行多线程处理的更大背景。
【问题讨论】:
-
[MyDataClass parseData:element]在做什么?它使用哪个上下文?[self saveContext]在做什么?它使用哪种上下文? -
@DanielEggert - 它接受 JSON 解析 API 提供的
NSDictionary并将其转换为NSManagedObject子类。它对存储执行获取请求,以查看对象是否已存储;如果是,它会更新,如果不是,它会创建。它不会在 MOC 上调用save。它使用的 MOC 是从我的数据管理器的managedObjectContext属性中获得的,该属性为每个访问它的线程返回一个不同的 MOC 实例。 -
@DanielEggert -
[self saveContext]在 MOC 上为调用它的线程调用save。这会触发一个保存通知,这使它成为我的mergeChanges方法,该方法合并我拥有的所有 MOC 的给定更改,除了正在调用mergeChanges的线程(正在合并更改的那个) .
标签: objective-c ios core-data nsmanagedobjectcontext