两天以来,我试图让Core Data与多个线程一起工作。我尝试了NSOperations的标准线程限制方法,合并通知,使用objectWithId,每个线程的上下文字典,但我仍然遇到奇怪的死锁,不一致异常和一堆其他令人讨厌的东西。它让我发疯了...而且当两个线程都可以对共享持久存储进行更改时,我找不到关于如何在两个线程中管理上下文的单个示例或解释...
我尝试使用新的iOS 5方法,这应该更容易,但我仍然会遇到错误。第一个问题是保存上下文时的死锁。我删除了所有不必要的代码,并且在执行此代码时足够快(通过快速点击按钮)获得死锁:
NSManagedObjectContext *context = [StoreDataRetriever sharedRetriever].managedObjectContext;
for (int i = 0; i < 5; i++) {
NSError *error = nil;
NSLog(@"Main thread: %@, is main? %d", [NSThread currentThread], [NSThread isMainThread]);
BOOL saveOK = [context save:&error];
if (!saveOK) {
NSLog(@"ERROR!!! SAVING CONTEXT IN MAIN");
}
[context performBlock:^{
NSLog(@"Block thread: %@", [NSThread currentThread]);
NSError *error = nil;
BOOL savedOK = NO;
savedOK = [context save:&error];
if (!savedOK) {
NSLog(@"ERROR!!! SAVING CONTEXT IN BLOCK");
}
}];
}
数据库没有其他更改,没有,只保存上下文。这段代码有什么问题?应该怎么样?
注意:[StoreDataRetriever sharedRetriever] .managedObjectContext是在appDelegate中使用initWithConcurrencyType创建的:NSPrivateQueueConcurrencyType。
答案 0 :(得分:2)
该代码发生了什么?您正在同步保存线程上的上下文,然后在上下文专用队列上安排保存。 5次。所以基本上,你可能有两个保存操作,一个是同步的,一个是异步的,相互冲突。
这显然是一个问题。您不应该使用该队列之外的私有队列保存上下文。如果上下文队列中没有调度块,它将与当前上下文实现一起使用。但这仍然是错误的。
…
for (int i = 0; i < 5; i++) {
NSLog(@"Main thread: %@, is main? %d", [NSThread currentThread], [NSThread isMainThread]);
__block NSError *error = nil;
__block BOOL saveOK = YES;
[context performBlockAndWait: ^{
saveOK = [context save: &error];
}];
if (!saveOK) {
NSLog(@"ERROR!!!");
}
…
使用该代码,您可以在同一个线程上同步执行保存操作 - 感谢GCD - 保留上下文切换和同步内容,并且没有任何同时在该上下文上运行两个操作的风险。
使用NSMainQueueConcurrencyType时也适用相同的规则,但有异常。该队列仅绑定到主线程和主线程。您可以使用带有performBlock和performBlockAndWait(如NSPrivateQueueConcurrencyType)的任何线程使用主队列在上下文中调度块,并且(异常:)可以直接在主线程上使用上下文。
NSConfinementConcurrencyType将上下文绑定到特定线程,您不能使用GCD或块来处理这样的上下文,只能处理绑定线程。到目前为止,使用这种并发模型的理由很少。如果必须,请使用它,但如果你不是必须的话,请不要使用它。
修改强>
这是一篇关于多上下文设置的非常好的文章:http://www.cocoanetics.com/2012/07/multi-context-coredata/