【问题标题】:Is a GCD dispatch queue enough to confine a Core Data context to a single threadGCD 调度队列是否足以将核心数据上下文限制到单个线程
【发布时间】:2011-12-04 20:24:53
【问题描述】:

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

也许天真地,然后我想我会简单地创建一个专用的 GCD 调度队列来处理核心数据(或者甚至,如果需要,有多个调度队列,每个调度队列都有自己的核心数据上下文)。这本来很简单。

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

我已经阅读了许多相关的问题和答案,例如Core Data and threads / Grand Central Dispatch,但我仍然有些困惑。该问题的公认答案是使用 GCD 队列,确实确保在每个线程上创建一个新上下文,但没有指出这样做的必要性。另一个答案说“您可以在名为 com.yourcompany.appname.dataaccess 的队列上执行所有 CoreData 工作”似乎暗示只要 Core Data 工作仅限于一个 GCD 调度队列,那么一切都很好。也许不是。

【问题讨论】:

    标签: ios core-data thread-safety grand-central-dispatch


    【解决方案1】:

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


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

    更长的答案:

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

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

    请参阅this questionanswer 以获得更详细的说明。

    这就是 Core Data 一直以来的工作方式。如果您决定在多个线程中使用单个上下文,Core Data Programming Guide 的 Concurrency with Core Data 部分提供了有关如何进行的建议。它主要讨论在访问上下文时需要非常小心地锁定上下文。然而,所有锁定的重点是确保两个或多个线程不会尝试同时使用上下文。使用序列化调度队列可以实现相同的目标:因为队列中一次只执行一个任务,所以不可能有两个或多个任务同时尝试使用上下文。

    【讨论】:

    • 很好的答案,但在链接线程中,bbum 的困惑似乎暗示 MOC 对象本身不能跨越线程边界,即使访问是序列化的。 Ben 明确说明了这一点,那么我们应该做什么?
    • @SedateAlien,在回答的开头,Ben 指出了documentation 中的注释:注意: 您可以使用线程,串行操作队列或并发调度队列。为了简洁起见,本文始终使用“线程”来指代其中的任何一个。”因此,在他的答案的其余部分或文档中,您可以将 serial dispatch queue 替换为 thread。同样,重要的是避免同时对不同的事物使用相同的上下文。上述三种机制中的任何一种都可以用来实现这一点。
    • 感谢您的所有帮助。我想这是一个小知识很危险的例子:我知道 MOC 知道它被实例化的线程并且知道 GCD 使用线程池并且无法想象它们如何一起工作,但是现在我接受它会起作用。再次感谢。
    • @Caleb,感谢您的回答和更详细解释的链接。它肯定有帮助。我认为我的部分困惑是,我从文档中了解到,这不仅仅是避免同时修改上下文的问题——还有其他深刻而神秘的事情正在发生。你的回答很有帮助,我一定会尽量坚持 Ben 的建议,尊重事情的复杂性。
    • @Caleb,非常感谢!这实际上开始对我有点意义。我没有对串行调度队列与并发调度队列之间的区别给予足够的关注。您的简短回答更加清楚。
    【解决方案2】:

    相信你是对的; GCD 不保证运行队列的线程。发送到队列的块和函数调用将一次运行一个,但如果 Core Data 对当前线程执行某些操作,例如安装一个运行循环源或观察者,事情可能不会按预期工作。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-08
      • 2016-07-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多