核心数据和竞争条件

时间:2011-11-18 06:55:06

标签: ios multithreading ipad core-data race-condition

问题:如何在两个不同的线程中对同一NSManagedObject进行Core Date合并更改?线程会更改不同的属性,这些属性的任意组合都是有效的。

有一个应用程序(至少)有两个线程, UI线程后台线程。 所谓的DocumentNSManagedObject的子类。 Document有三个属性,attrAattrBattrC

后台主题读取attrA并写入attrB,如:

doc.attbB = md5(doc.attrA);

(实际上,它更复杂,更耗时,但你明白了。)

UI线程向用户显示所有attrAattrBattrC,并允许用户更改attrA和{{1 }}。 (有些时候attrC的值无效。)

我强调只有 UI线程写入attrB,只有后台线程写入attrA

现在,用户在attrB的计算完成之前更改了attrC后台线程尝试保存attrB并收到错误消息。 我现在做的是:

attrB

在一般情况下,这不起作用。

首先,如果在这些行之间进行更多更改会发生什么:

if(!saved) {
    // I did try to check that it's *that* kind of error,
    // but that's iOS5-specific, while I need 4.3
    // (comments on error type checking in 4.3 are welcome).
    // Anyway, finally it was this:
    id tmp = doc.attrB;
    [[doc managedObjectContext] refreshObject:doc mergeChanges:NO];
    doc.attrB = tmp;
    BOOL saved = [context save:&error];
    // NSLog if it still failed
}

是的,我可以暂时替换,但这不是一般案例解决方案。如果这些变化反复发生,这可能是一个无限循环。

第二, UI线程也可能正试图保存一些东西。在其中一个类中,我看到了用于合并更改的代码,

    [[doc managedObjectContext] refreshObject:doc mergeChanges:NO];
    doc.attrB = tmp;
    // more changes happen here!!!
    BOOL saved = [context save:&error];

据我了解,#pragma mark Changes Propagation - (void)__contextDidSave:(NSNotification*)notification { [parent performSelectorOnMainThread:@selector(__mergeChanges:) withObject:notification waitUntilDone:NO]; } - (void)__mergeChanges:(NSNotification*)notification { [objectContext mergeChangesFromContextDidSaveNotification:notification]; if (parent) { [parent __mergeChanges:notification]; } } 将在正确的主题上运行,但不会立即运行;在后台线程更改__mergeChanges之后, UI线程可能会尝试保存attrAattrC中的更改,但在attrB运行之前。

这是一种典型的竞争条件。

问题:如何正确使核心日期合并在两个不同的线程中对同一__mergeChanges进行合并? (线程更改了不同的属性,这些属性的任何组合都有效,但Core Data不知道。)

1 个答案:

答案 0 :(得分:0)

我不是核心数据方面的专家,但我猜你可以使用synchronized指令:

// UI Thread
@synchronized(AN_INSTANCE_OF_ANY_OBJECT) {
    // your first thread's code goes here
}

// Background Thread
@synchronized(THE_SAME_INSTANCE_OF_THE_OBJECT) {
    // your second thread's code goes here
}

如果任何其他线程正在执行其作用域,这应该会停止执行任何线程,请注意该实例应该是相同的,所以如果你不能访问该实例,你可以使用一个公共静态实例,这通常可以帮助我做这个技巧