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

Ios GCD调度队列是否足以将核心数据上下文限制在单个线程中,ios,core-data,thread-safety,grand-central-dispatch,Ios,Core Data,Thread Safety,Grand Central Dispatch,我开始认为我的问题的答案是“不”,但我仍然对此感到困惑和不确定。所以请确认。我已经学会了在使用多线程的核心数据时要小心。NSManagedObjectContext对象不能跨越线程边界。作为一个线程和核心数据的新手,我很高兴地发现GCD应该使其中一些变得更容易 也许很天真,我当时认为我应该简单地创建一个专用的GCD调度队列来处理核心数据(或者,如果需要,甚至可以有多个调度队列,每个队列都有自己的核心数据上下文)。这很简单 但现在我意识到GCD调度队列的一大优势是它可以根据需要管理和使用多个线程。

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

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

但现在我意识到GCD调度队列的一大优势是它可以根据需要管理和使用多个线程。因此——如果我理解正确的话——我交给同一个调度队列的任务可能会在不同的线程中运行,可能会将一个核心数据上下文从一个线程交给另一个线程,并导致出现问题。是这样吗


例如,我已经阅读了许多相关的问题和答案,但我仍然有些困惑。这个问题的公认答案是使用GCD队列,它确实确保在每个线程上创建一个新的上下文,但没有指出这样做的必要性。另一个答案是“您可以在一个名为com.yourcompany.appname.dataaccess的队列上执行所有CoreData工作”,这似乎意味着只要核心数据工作仅限于一个GCD调度队列,那么一切都是正常的。也许不是。

更新:正如@adib在评论中指出的那样,在iOS 9和MacOS X 10.11中,序列化托管对象上下文访问的方法已经改变
NSConfinementConcurrencyType
,线程限制策略,现在被弃用,取而代之的是
NSPrivateQueueConcurrencyType
NSMainQueueConcurrencyType
。换句话说,停止使用线程并发访问核心数据对象,而是开始使用GCD。您应该使用主调度队列或与MOC关联的调度队列,这取决于您如何配置MOC,而不是您自己创建的队列。使用NSManagedObject的
-performBlock:
-performBlock和wait:
方法很容易做到这一点


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

更长的回答:

使用GCD队列对该问题的公认答案确实可以确保 在每个线程上创建一个新的上下文,但没有指出 这样做的必要性

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

有关更详细的说明,请参见和


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

好吧,你是对的;GCD不保证运行队列的线程。发送到队列的块和函数调用将一次运行一个,但如果核心数据对当前线程执行某些操作,例如安装运行循环源或观察者,则可能无法按预期工作


但是,在Mac OS X 10.7上,可以将NSManagedObjectContext设置为在主线程、单独线程或专用队列中运行。

回答得很好,但在链接线程中,bbum的混淆似乎意味着MOC对象本身无法跨越线程边界,即使访问是串行的。Ben特别指出了这一点,那么我们应该怎么做呢?@Sasterealien在回答的早期,Ben指出了中的注释:注意:您可以使用线程、串行操作队列或调度队列来实现并发性。为了简洁起见,本文通篇使用“线程”来指代其中任何一个。因此,在他回答的其余部分或文档中,您可以用串行调度队列代替线程。同样,重要的是避免在同一时间为不同的事情使用相同的上下文。上述三种机制中的任何一种都可以用来实现这一目标。感谢您的帮助。我想这是一个小知识是危险的例子:我知道MOC知道它被实例化的线程,知道GCD使用线程池,无法想象它们如何协同工作,但现在我承认它会工作。再次感谢。@Caleb,谢谢你的回答和更详细解释的链接。这肯定有帮助。我想我的困惑的一部分是,我从文档中感觉到,这不仅仅是一个避免同时修改的问题