GCD 调度队列是否足以将 Core Data 上下文限制到单个线程
我开始认为我的问题的答案是“不”,但我仍然对此感到困惑和不确定。所以请确认一下。我已经了解到在使用具有多个线程的 Core Data 时需要小心。 NSManagedObjectContext 对象不得跨越线程边界。作为线程和核心数据的新手,我很高兴地发现 GCD 应该使这一切变得更容易。
也许天真地,我认为我会简单地创建一个专用的 GCD 调度队列来处理核心数据(或者甚至,如果需要的话,拥有多个调度队列,每个队列都有自己的核心数据上下文)。那本来很简单。
但现在我意识到 GCD 调度队列的一大优点是它可以根据需要管理和使用多个线程。因此,如果我理解正确的话,我将任务移交给同一个调度队列,最终可能会在不同的线程中运行,可能会将核心数据上下文从一个线程移交给另一个线程,从而导致出现问题。是这样吗?
我读过许多相关的问题和答案,例如核心数据和线程/Grand Central Dispatch ,但我仍然有些困惑。该问题的公认答案是使用 GCD 队列,确实确保在每个线程上创建一个新的上下文,但没有指出这样做的必要性。另一个答案说“您可以在名为 com.yourcompany.appname.dataaccess 的队列上执行所有 CoreData 工作”,这似乎意味着只要 Core Data 工作仅限于一个 GCD 调度队列,那么一切都可以。也许不是。
I'm beginning to think the answer to my question is 'No', but I'm still confused and uncertain about this. So please confirm. I've already learned the need to be careful when using Core Data with multiple threads. NSManagedObjectContext objects must not cross thread boundaries. Being a newbie with both threads and Core Data, I happily found that GCD should make some of this easier.
Naively perhaps, I then thought I would simply create a dedicated GCD dispatch queue for dealing with Core Data (or even, if needed, have multiple dispatch queues each with its own core data context). That would have been simple.
But now I realize that one big advantage of GCD dispatch queues is that it manages and makes use of multiple threads as needed. So - if I understand this right - tasks I hand off to one and the same dispatch queue, could end up running in different threads, potentially handing off a core data context from one thread to another, and having things go wrong. Is that right?
I've read many related questions and answers, for example Core Data and threads / Grand Central Dispatch, but I remain somewhat confused. The accepted answer to that question, using GCD queues, does ensure that a new context is created on each thread, but does not point out the necessity of doing this. Another answer says "You could execute all CoreData work on a queue named com.yourcompany.appname.dataaccess" seeming to imply that as long as the Core Data work is confined to one GCD dispatch queue, then all is OK. Maybe it is not.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
更新:正如 @adib 在评论中指出的那样,序列化托管对象上下文 (MOC) 访问的方法在 iOS 9 和 MacOS X 10.11 中发生了变化。线程限制策略
NSConfinementConcurrencyType
现已弃用,取而代之的是NSPrivateQueueConcurrencyType
和NSMainQueueConcurrencyType
。换句话说,停止使用线程来并发访问 Core Data 对象,而开始使用 GCD。您应该使用主调度队列或与 MOC 关联的队列,具体取决于您如何配置 MOC,而不是您自己创建的队列。使用 NSManagedObject 的-performBlock:
或-performBlockAndWait:
方法可以轻松做到这一点。简短回答:使用串行调度队列可以提供对托管对象上下文的序列化访问,这是实现“线程限制”策略的可接受方法,即使 GCD 实际上可能使用多个线程。
更长的答案:
您需要记住的重要一点是,您必须避免同时从两个不同的线程修改托管对象上下文。这可能会使上下文陷入不一致的状态,并且不会产生任何好处。因此,您使用的调度队列的种类非常重要:并发调度队列将允许多个任务同时进行,如果它们都使用相同的上下文,您就会遇到麻烦。另一方面,如果使用串行调度队列,两个或多个任务可能会在不同的线程上执行,但任务将按顺序执行,并且一次只会运行一个任务。这与在同一线程上运行所有任务非常相似,至少就维持上下文的一致性而言是如此。
请参阅此问题和答案了解更多信息详细解释。
这就是 Core Data 一直以来的工作方式。 与核心数据的并发 部分提供了有关如何在您决定在多个线程中使用单个上下文时继续操作的建议。它主要讨论的是每次访问上下文时都需要非常小心地锁定它。不过,所有锁定的目的是确保两个或多个线程不会尝试同时使用上下文。使用序列化调度队列可以实现相同的目标:因为队列中一次只有一个任务执行,所以两个或多个任务不可能同时尝试使用上下文。
Update: As @adib points out in a comment, the approach to serialized managed object context (MOC) access has changed in iOS 9 and MacOS X 10.11.
NSConfinementConcurrencyType
, the thread confinement strategy, is now deprecated in favor ofNSPrivateQueueConcurrencyType
andNSMainQueueConcurrencyType
. In other words, stop using threads for concurrent access to Core Data objects and instead start using GCD. You should use either the main dispatch queue or the one associated with the MOC, depending on how you configure the MOC, and not a queue of your own creation. This is easy to do using NSManagedObject's-performBlock:
or-performBlockAndWait:
methods.Short answer: Using a serial dispatch queue can provide serialized access to a managed object context, and that's an acceptable way to implement the "thread confinement" strategy even though GCD may actually employ multiple threads.
Longer answer:
The big thing you need to remember is that you must avoid modifying the managed object context from two different threads at the same time. That could put the context into an inconsistent state, and nothing good can come of that. So, the kind of dispatch queue that you use is important: a concurrent dispatch queue would allow multiple tasks to proceed simulaneously, and if they both use the same context you'll be in trouble. If you use a serial dispatch queue, on the other hand, two or more tasks might execute on different threads, but the tasks will be executed in order, and only one task will run at a time. This is very similar to running all the tasks on the same thread, at least as far as maintaining the context's consistency goes.
See this question and answer for a much more detailed explanation.
This is how Core Data has always worked. The Concurrency with Core Data section of the Core Data Programming Guide gives advice on how to proceed if you do decide to use a single context in multiple threads. It talks mainly about the need to be very careful to lock the context any time you access it. The point of all that locking, though, is to ensure that two or more threads don't try to use the context simultaneously. Using a serialized dispatch queue achieves the same goal: because only one task in the queue executes at a time, there's no chance that two or more tasks will try to use the context at the same time.
AFAIK你是对的; GCD 不保证队列运行所在的线程。发送到队列的块和函数调用将一次运行一个,但如果 Core Data 对当前线程执行某些操作,例如安装运行循环源或观察者,则事情可能不会按预期工作。
但是,在 Mac OS X 10.7 上,NSManagedObjectContext 可以设置为在主线程、单独线程或专用队列中运行。
AFAIK you're correct; GCD doesn't make guarantees about the thread in which the queue is run. Blocks and function calls sent to the queue will be run one at a time, but if Core Data does something with the current thread, e.g. installs a run loop source or observer, things probably won't work as intended.
However, on Mac OS X 10.7, NSManagedObjectContext can be set to run on the main thread, on a separate thread, or in a private queue.