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 CoreData and threads / GCD, 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.

2 Answers
2

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 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.

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.

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.

Great answer, but in the linked thread, bbum's confusion seems to imply that the MOC object itself cannot cross thread boundaries, even if access is serialised. Ben specifically states this, so what are we expected to do?
–
Sedate AlienOct 11 '11 at 3:03

2

@SedateAlien, early in his answer, Ben points to the note in the documentation that says: Note: You can use threads, serial operation queues, or dispatch queues for concurrency. For the sake of conciseness, this article uses “thread” throughout to refer to any of these." So, in the remainder of his answer, or in the documentation, you can substitute serial dispatch queue for thread. Again, the essential thing is to avoid using the same context for different things at the same time. Any of the three mechanisms mentioned can be used to achieve that.
–
CalebOct 11 '11 at 3:18

1

Thanks for all your help. I suppose this is a case of a little knowledge being a dangerous thing: I knew that the MOC was aware of the thread it was instantiated on and knew that GCD employed thread pools and couldn't imagine how they could work together, but now I accept that it will work. Thanks again.
–
Sedate AlienOct 11 '11 at 3:38

1

@Caleb, thanks for the answer and the link with more detailed explanation. It helps for sure. I think part of my confusion is that I get the sense from the documentation that it is more than a matter of just avoiding simultaneous modifications to the context -- that there are other deep and mysterious things going on. Your answer helps, and I will definitely try to stick with Ben's recommendation and have respect for the complexity of the matter.
–
reneOct 11 '11 at 3:56

1

@Caleb, thanks a lot! This is actually starting to make a bit of sense to me. I had not given enough attention to the distinction between serial versus concurrent dispatch queues. Your short answer clarifies even more.
–
reneOct 12 '11 at 13:41

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.