【问题标题】:Hang in mergeChangesFromContextDidSaveNotification (and merge conflicts)挂起 mergeChangesFromContextDidSaveNotification (和合并冲突)
【发布时间】:2012-09-06 18:27:56
【问题描述】:

总结:当我在多线程场景中调用[myMOC mergeChangesFromContextDidSaveNotification:notification] 时,我的应用程序挂起。

详细情况是这样的:

我的应用程序在首次启动时从服务器下载大量数据并将其存储在 Core Data 中。它分为几个部分。解析整个事情需要几秒钟,大部分时间都花在了其中的两个块上。所以为了加快速度,我已经并行化了这两个块。这是它的样子:

NSArray *arr = [self parseJsonData:downloadedNSData]; //turns NSData into JSON array
                     //using NSJSONSerialization
NSMutableArray __block *first = [[NSMutableArray alloc]init];
NSMutableArray __block *second = [[NSMutableArray alloc]init];

//put half of arr in first and half in second with a loop

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dipatch_group_create(); 
dispatch_group_async(group, queue, ^
   {
      for (id element in first)
      {
         [MyDataClass parseData:element]; //create NSManagedObject subclass, save
      }
      [self saveContext];
   });

    dispatch_group_async(group, queue, ^
   {
      for (id element in second)
      {
         [MyDataClass parseData:element]; //create NSManagedObject subclass, save
      }
      [self saveContext];
   });

  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

[self saveContext] 在 MOC 上调用 save。这当然是在多个线程上完成的,所以我需要为每个线程创建一个单独的ManagedObjectContext。这是通过通过此对象 (self) 上的属性公开 MOC 来实现的,该属性维护线程名称的NSMutableDictionary(在NSThread 上调用description)到NSManagedObjectContexts。访问时,如果没有[NSThread currentThread] 的 MOC,它会创建一个新的 MOC,将其添加到字典中并存储它。创建每个 MOC 时,我都会订阅其更改通知:

NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(mergeChanges:)
      name:NSManagedObjectContextDidSaveNotification object:createdMOC];

mergeChanges 中,我循环浏览我的上下文字典,并在所有上下文上调用mergeChangesFromContextDidSaveNotification,但发生这种情况的线程除外。更具体地说,我使用[performSelector:onThread:withObject:waitUntilDone:] 让每个 MOC 在为其创建的线程上执行此操作。

我还使用NSLock 围绕[myMOC save] 和我的mergeChanges 方法进行锁定。如果我没有锁定这些,我会收到“Cocoa error 133020”,apparently 表示合并更改时出错。

所以,这就是我的日志记录告诉我正在发生的事情:

  • 线程 1 获取要保存的锁并开始保存
  • 线程 1 上出现合并上下文通知。线程 1 获取用于合并更改的锁并开始该过程。它为主线程合并 MOC 的更改,然后在为后台线程执行 MOC 之一时挂起。
  • 线程 2 开始保存但从未获取保存锁,因为另一个线程在尝试合并更改时卡住了。

那么,为什么在合并更改时它会挂起?有没有更好的方法来处理这种情况?

更新:我尝试在我的 [MOC save] 调用周围使用 [persistentStoreCoordinator lock] 而不是仅使用 NSLock 进行锁定,但无济于事。我还尝试在调用 [self saveContext] 之前在 dispatch_group_async 调用之一中添加 [NSThread sleepForTimeInterval:2],但没有帮助。

更新 2: 也许更好的问题是为什么我会遇到合并冲突(Cocoa 错误 133020)。这是预期的吗?我是否正确地进行了合并(合并到除了一个保存之外的所有上下文)?

更新 3:我已发布 another question 以解决我如何进行多线程处理的更大背景。

【问题讨论】:

  • [MyDataClass parseData:element] 在做什么?它使用哪个上下文? [self saveContext] 在做什么?它使用哪种上下文?
  • @DanielEggert - 它接受 JSON 解析 API 提供的 NSDictionary 并将其转换为 NSManagedObject 子类。它对存储执行获取请求,以查看对象是否已存储;如果是,它会更新,如果不是,它会创建。它不会在 MOC 上调用save。它使用的 MOC 是从我的数据管理器的 managedObjectContext 属性中获得的,该属性为每个访问它的线程返回一个不同的 MOC 实例。
  • @DanielEggert - [self saveContext] 在 MOC 上为调用它的线程调用 save。这会触发一个保存通知,这使它成为我的 mergeChanges 方法,该方法合并我拥有的所有 MOC 的给定更改,除了正在调用 mergeChanges 的线程(正在合并更改的那个) .

标签: objective-c ios core-data nsmanagedobjectcontext


【解决方案1】:

当您创建 NSManagedObject 子类时,您会将其插入到上下文中,因此您只能在属于该上下文的队列/线程上执行此操作。

在上下文中调用 -save 也是如此。

从您的代码看来,您正在插入两个对象——每个对象都在它们自己的线程上。那是行不通的。

【讨论】:

  • 我也不认为我在做。每个线程都有自己的上下文,并且托管对象不会在线程之间共享。
  • 不过,它们被插入到不同线程上的不同上下文中。这应该可行,对吧?
  • 我已经发布了一个link 来解决关于我如何进行多线程的更大背景的问题。
【解决方案2】:

我已经发布了this question,以更好地记录我的情况。我认为当前这个问题的范围有点窄。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-18
    • 1970-01-01
    • 2020-02-28
    • 2011-08-20
    • 2017-03-04
    • 2011-03-13
    • 2017-07-24
    相关资源
    最近更新 更多