使用并发类型NSPrivateQueueConcurrencyType保存上下文时的死锁

时间:2012-07-20 13:46:36

标签: ios core-data

两天以来,我试图让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。

1 个答案:

答案 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/