核心数据多线程获取记录

Posted

技术标签:

【中文标题】核心数据多线程获取记录【英文标题】:Core data multithreading fetch record 【发布时间】:2014-02-21 12:14:22 【问题描述】:

我对 CoreData 中的多线程有一个疑问。如果我们使用多线程,我们应该使用单独的 NSManagedObjectContext 来插入新数据或更新,否则我们可以使用父子上下文方法。但我只创建新的 NSManagedObjectContext 。我的问题是 - 我是否应该使用单独的 NSManagedObjectContext 来获取甚至在后台线程中?如果不是(即我们只能使用主队列 NSManagedObjectContext),那么为什么会出现 __psynch_mutexwait 错误?

谢谢

【问题讨论】:

显示你正在使用的代码,你提供的描述真的没有解释太多 【参考方案1】:

首先,Core Data 是线程安全的。但是你必须遵守规则:

    NSManagedObjectContext 是线程绑定的。您只能在分配给它的线程上使用它。 -init 导致将上下文分配给创建它的线程。使用 -initWithConcurrencyType: 将允许您创建与其他线程/队列关联的上下文。 与NSManagedObjectContext 关联的任何NSManagedObject 都绑定到与其来自的上下文相同的线程/队列 没有第三条规则

您可以在线程之间传递NSManagedObjectID 实例,但必须遵守规则 1 和 2。根据您的描述,我认为您违反了这些规则。

我个人也不推荐使用 NSManagedObjectID。有更好的解决方案。 ——马库斯·S·扎拉

Marcus,这是我读过的关于 Core Data 线程的最简洁的解释。自从它推出以来一直使用它,有几天我仍然把这些规则弄错了!你提到了“更好的解决方案”——你能详细说明一下吗?

我非常不信任NSManagedObjectID 的使用。在许多情况下,它不会从一个应用程序生命周期到另一个应用程序生命周期保持不变。最初,根据文档,我们(一般的 Cocoa 开发人员)认为这是为我们生成的神话主键。事实证明这是不正确的。

在具有父/子上下文的现代开发中,情况更加混乱,我们需要注意一些有趣的陷阱。鉴于目前的情况,我比以前更不喜欢它。那我们用什么?

我们应该自己生成。它不需要太多。如果您的数据还没有来自服务器的主键(很常见的是来自基于 Ruby 的服务器的 id)然后创建一个。我喜欢称它为guid,然后有一个类似于此的-awakeFromInsert

- (void)awakeFromInsert

  [super awakeFromInsert];
  if (![self primitiveValueForKey:@"guid"]) 
    [self setPrimitiveValue:[[NSProcessInfo processInfo] globallyUniqueString] forKey:@"guid"];
  

注意:此代码是在网络浏览器中编写的,可能无法编译。

您检查该值是因为 -awakeFromInsert 在每个上下文中调用一次。然后我通常会在我的NSManagedObject 实例上使用一种方便的方法,类似于:

@implementation MyManagedObject

+ (MyManagedObject*)managedObjectForGUID:(NSString*)guid inManagedObjectContext:(NSManagedObjectContext*)context withError:(NSError**)error

  NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:[self entityName]];
  [fetch setPredicate:[NSPredicate predicateWithFormat:@"guid == %@", guid]];

  NSArray *results = [context executeFetchRequest:request error:error];
  if (!results) return nil;

  return [results lastObject];


@end

注意:此代码是在网络浏览器中编写的,可能无法编译。

这将错误处理和上下文/线程控制留给开发人员,但提供了一种方便的方法来检索当前上下文中的对象,并允许我们将对象从一个上下文“反弹”到另一个上下文。

这比 -objectWithID: 慢,应该小心使用,并且只有在保存后需要将对象从一个上下文反弹到另一个上下文的情况下才能将其移到堆栈上。

和我做的大多数事情一样; 这不是通用解决方案。这是一个应该根据每个项目进行调整的基线。

【讨论】:

重要的是要注意 NSManagedObjectID 在线程安全之前必须是永久的(如果正确的话)? 不,这是不正确的。 NSManagedObjectID 是一个可变对象,当保存发生并且 ID 从临时变为永久时,Core Data 会对其进行变异。我个人也不推荐使用NSManagedObjectID。有更好的解决方案。 Marcus,这是我读过的关于 Core Data 线程的最简洁的解释。自从它推出以来一直使用它,有几天我仍然把这些规则弄错了!你提到“更好的解决方案”——你能详细说明一下吗? 您说没有第三条规则,但是如果您在每次使用之前lockunlock 它(包括NSManagedObject 的隐式使用),则允许您从多个线程中使用NSManagedObjectContext s)。 @HeathBorders 不正确。您可以通过从多个带锁的线程访问NSManagedObjectContext 来“逃脱”,但它有损坏和崩溃的风险。它违反了 Core Data 的线程包含规则,在任何情况下都应该这样做。

以上是关于核心数据多线程获取记录的主要内容,如果未能解决你的问题,请参考以下文章

多线程违规核心数据

核心数据多线程——我做错了啥

python核心编程学习记录之多线程编程

另一种多线程核心数据使用

核心数据和多线程在保存时崩溃

实体框架核心和多线程