GCD调度队列是否足以将核心数据上下文限制在单个线程中?



我开始认为我的问题的答案是"不",但我仍然感到困惑和不确定。所以请确认。我已经学会了在使用多线程的核心数据时要小心。NSManagedObjectContext对象不能跨越线程边界。作为一个既有线程又有核心数据的新手,我很高兴地发现GCD可以让这变得更容易。

我当时天真地认为,我可以简单地创建一个专用的GCD调度队列来处理核心数据(或者,如果需要的话,甚至可以有多个调度队列,每个队列都有自己的核心数据上下文)。那就简单了。

但是现在我意识到GCD调度队列的一大优点是它可以根据需要管理和使用多个线程。所以——如果我理解正确的话——我交给同一个调度队列的任务,最终可能会在不同的线程中运行,可能会将核心数据上下文从一个线程传递到另一个线程,从而出现问题。对吗?

我读了很多相关的问题和答案,例如Core Data和threads/Grand Central Dispatch,但我仍然有些困惑。对于这个问题,公认的答案是使用GCD队列,它确保在每个线程上创建一个新的上下文,但没有指出这样做的必要性。另一个答案是"你可以在一个名为com.yourcompany.appname的队列上执行所有CoreData工作。"数据访问"似乎意味着只要核心数据工作被限制在一个GCD调度队列中,那么一切都没问题。也许不是。

Update:正如@adib在评论中指出的那样,序列化管理对象上下文(MOC)访问的方法在iOS 9和MacOS X 10.11中发生了变化。NSConfinementConcurrencyType,线程限制策略,现在被弃用,取而代之的是NSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType。换句话说,停止使用线程并发访问Core Data对象,而开始使用GCD。您应该使用主调度队列或与MOC关联的队列,这取决于您如何配置MOC,而不是您自己创建的队列。这很容易做到使用NSManagedObject的-performBlock:-performBlockAndWait:方法。


简短的回答:使用串行调度队列可以提供对托管对象上下文的序列化访问,这是实现"线程限制"的一种可接受的方式。策略,即使GCD实际上可能使用多个线程。

长答:

这个问题的可接受的答案,使用GCD队列,确实确保在每个线程上都创建了一个新的上下文,但没有指出做这件事的必要性。

您需要记住的一件大事是,您必须避免同时从两个不同的线程修改托管对象上下文。这可能会使上下文处于不一致的状态,这不会有什么好结果。因此,您使用的调度队列的类型非常重要:并发调度队列将允许多个任务同时进行,如果它们都使用相同的上下文,您将遇到麻烦。另一方面,如果使用串行调度队列,则两个或多个任务可能在不同的线程上执行,但这些任务将按顺序执行,并且一次只运行一个任务。这与在同一个线程上运行所有任务非常相似,至少在维护上下文的一致性方面是这样。

请参阅这个问题和答案,以获得更详细的解释。

这就是Core Data一直以来的工作方式。核心数据编程指南的核心数据并发部分给出了如何处理的建议,如果你决定在多个线程中使用一个上下文。它主要讲的是在访问上下文时要非常小心地锁定上下文。但是,所有这些锁定的目的是确保两个或更多线程不会试图同时使用上下文。使用序列化的调度队列实现了相同的目标:因为队列中一次只执行一个任务,所以不可能有两个或更多的任务同时尝试使用上下文。

我想你是对的;GCD不保证队列在哪个线程中运行。发送到队列的块和函数调用将一次运行一个,但如果Core Data对当前线程做了一些事情,例如安装一个运行循环源或观察者,事情可能不会像预期的那样工作。

但是,在Mac OS X 10.7中,NSManagedObjectContext可以设置为在主线程、单独线程或私有队列中运行

最新更新