一个典型的设置:我们有一个mainMOC
的主线程和一个自己的后台线程backgroundMOC
。后台线程通过将块调度到backgroundQueue
来对backgroundMOC
执行只读操作。如何在跨线程合并更改时防止竞争条件?
的backgroundMOC
需要合并从mainMOC
的变化,所以我们注册NSManagedObjectContextDidSaveNotification
,然后像做
- (void)mainMocDidSave:(NSNotification *)notification {
dispatch_async(backgroundQueue, ^{
[backgroundMoc mergeChangesFromContextDidSaveNotification:notification];
});
}
比方说,用户删除的mainMOC
的对象。上面的代码对我来说似乎并不安全,因为合并将在未来的某个时间点完成。在合并完成之前,backgroundQueue
上的块可能仍会尝试使用已删除的对象。
明显的解决方案是使用dispatch_sync
代替(或performBlockAndWait
,performSelector:OnThread:...
)。从我在互联网上看到的代码片段来看,这似乎是每个人都在做的事情。但是我对这个解决方案也不舒服。
名称NSManagedObjectContextDidSaveNotification
意味着保存已在通知发送时发生。所以相应的行已经从底层数据库中删除(假设一个sqlite存储)。 dispatch_sync
必须等待队列上的其他块完成才能合并更改,而这些其他块仍然可以尝试使用已删除的对象,从而产生NSObjectInaccessibleException
。
在我看来,要合并从一个线程/队列到另一个变化的正确方法是
- 订阅
NSManagedObjectContextWillSaveNotification
和NSManagedObjectContextDidSaveNotification
在后台线程。 - 对
NSManagedObjectContextWillSaveNotification
:清空backgroundQueue
并暂停向队列分派新块的任何操作。 - 对
NSManagedObjectContextDidSaveNotification
:同步合并更改。 - 恢复对后台队列的正常操作。
这是正确的方法还是我错过了什么?
作为后续:我不断收到死锁与上面的解决方案。看起来在某些情况下,主控线程在发送'NSManagedObjectContextWillSaveNotification'时已经获得了PSC上的锁定,这可能导致上面步骤2中所需的dispatch_sync调用在背景队列上仍有未完成的任务时一直等待。 – Lukas 2013-03-26 12:48:28