核心数据和竞争条件
问题:如何在两个不同的线程中合并对同一 NSManagedObject
所做的 Core Date 更改?线程改变不同的属性,这些属性的任意组合都是有效的。
有一个应用程序(至少)有两个线程:UI 线程 和后台线程。 所谓的 Document
是 NSManagedObject
的子类。 Document
具有三个属性:attrA
、attrB
和 attrC
。
后台线程读取attrA
并写入attrB
,例如:(
doc.attbB = md5(doc.attrA);
实际上,它更复杂且更耗时,但你得到主意)。
UI 线程向用户显示所有 attrA
、attrB
和 attrC
并允许用户更改 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线程可能会尝试保存attrA
和attrC
中的更改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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我不是核心数据方面的专家,但我猜你可以使用同步指令:
如果任何其他线程正在执行其范围,这应该停止任何线程的执行,请注意实例应该是相同的,所以如果你不能访问该实例,您可以使用公共静态实例,这通常可以帮助我完成此技巧。
I'm not an expert in core data but as I guess you can use the synchronized directive:
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.