核心数据和竞争条件

发布于 2024-12-16 02:42:33 字数 2394 浏览 3 评论 0原文

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

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

后台线程读取attrA并写入attrB,例如:(

doc.attbB = md5(doc.attrA);

实际上,它更复杂且更耗时,但你得到主意)。

UI 线程向用户显示所有 attrAattrBattrC 并允许用户更改 attrA 和 attrC 。 (有时 attrB 的值无效。)

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

现在,用户在 attrB 计算完成之前更改了 attrC后台线程尝试保存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
}

在一般情况下这是行不通的。

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

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

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

其次,UI 线程也可能正在尝试保存某些内容。在其中一个类中,我看到旨在合并更改的代码,

#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 运行之前。

这是一个经典的竞争条件。

问题:如何正确合并两个不同线程中对同一 NSManagedObject 所做的 Core Date 更改? (线程更改不同的属性,这些属性的任何组合都是有效的,但 Core Data 不知道这一点。)

Question: how do I make Core Date merge changes made to the same NSManagedObject in two different threads? The threads change different attributes, and any combination of these attributes is valid.

There is an application with (at least) two threads, the UI thread and the background thread.
A so-called Document is a subclass of NSManagedObject.
A Document has three attributes, attrA, attrB and attrC.

The background thread reads attrA and writes attrB, like:

doc.attbB = md5(doc.attrA);

(in practice, it is more complex and more time-consuming, but you get the idea).

The UI thread shows all attrA, attrB and attrC to the user and allows the user to change attrA and attrC. (And for some while the value of attrB is invalid.)

I underline that only UI thread writes to attrA, and only background thread writes to attrB.

Now, the user changes attrC before calculation of attrB is complete.
The background thread tries to save attrB and gets an error.
What I did for now is:

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
}

In the general case this will not work.

At first, what happens if more changes will be made between these lines:

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

Yes, I could replace if by a while, but it's not the general case solution. If the changes happen repeatedly, this may be an endless loop.

At second, the UI thread is also may be trying to save something. In one of the classes I see the code intended to merge changes,

#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];
    }
}

As I understand, __mergeChanges will be run on the right thread, but not immediately; it is possible that the UI thread will be trying to save changes in attrA and attrC after the background thread changed attrB but before __mergeChanges is run.

This is a classic race condition.

Question: how do I correctly make Core Date merge changes made to the same NSManagedObject in two different threads? (The threads change different attributes, and any combination of these attributes is valid, but Core Data does not know that.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

高冷爸爸 2024-12-23 02:42:33

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

// 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
}

如果任何其他线程正在执行其范围,这应该停止任何线程的执行,请注意实例应该是相同的,所以如果你不能访问该实例,您可以使用公共静态实例,这通常可以帮助我完成此技巧。

I'm not an expert in core data but as I guess you can use the synchronized directive:

// 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
}

this should stops the execution of any thread if any other is executing its scope, notice that the instance should be the same, so if you can not access that instance you can use a public static instance which usually helps me do this trick.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文