The following things are what I know & understand:
Global queue is a concurrent queue which can dispatch tasks to multiple threads.
The relationship between the main thread's run loop and the main dispatch queue is merely that they're both run on the main thread and that blocks dispatched to main queue are interleaved on the main thread with events processed on the main runloop.
As the Concurrency Programming Guide says:
The main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread. This queue works with the application’s run loop (if one is present) to interleave the execution of queued tasks with the execution of other event sources attached to the run loop. Because it runs on your application’s main thread, the main queue is often used as a key synchronization point for an application.
When dispatching to background thread, it does not create a NSRunLoop
for those worker threads. Nor do you generally need a run loop for these background threads. We used to have to create our own NSRunLoop
for background threads (e.g. when scheduling NSURLConnection
on background thread), but this pattern is not required very often anymore.
For things historically requiring run loops, there are often better mechanisms if running them on a background thread. For example, rather than NSURLConnection
, you'd now use NSURLSession
. Or, rather than NSTimer
on NSRunLoop
on background thread, you'd create a GCD timer dispatch source.
Regarding who "knows" the thread, the worker thread is identified when dispatched to a queue. The thread is not a property of the queue, but rather assigned to the queue when the queue needs it.
If you want to create a NSRunLoop
for a worker thread (which you generally shouldn't be doing, anyway), you create it and keep track of it yourself. And, when scheduling a thread with a run loop, I would be inclined to create the NSThread
myself and schedule the run loop on that, rather than tying up one of GCD's worker threads.
The main thread's run loop has a step in which it runs any blocks queued on the main queue. You might find this answer useful if you want to understand what the run loop does in detail.
GCD creates the threads for concurrent queues. A thread doesn't have a run loop until the first time something running on the thread asks for the thread's run loop, at which point the system creates a run loop for the thread. However, the run loop only runs if something on that thread then asks it to run (by calling -[NSRunLoop run]
or CFRunLoopRun
or similar). Most threads, including threads created for GCD queues, never have a run loop.
GCD manages a pool of threads and, when it needs to run a block (because it was added to some queue), GCD picks the thread on which to run the block. GCD's thread-choosing algorithm is mostly an implementation detail, except that it will always choose the main thread for a block that was added to the main queue. (Note that GCD will also sometimes use the main thread for a block added to some other queue.)
You can only get the run loop of the main thread (using +[NSRunLoop mainRunLoop]
or CFRunLoopGetMain
) or the run loop of the current thread (using +[NSRunLoop currentRunLoop]
or CFRunLoopGetCurrent
). If you need the run loop of some arbitrary thread, you must find a way to call CFRunLoopGetCurrent
on that thread and pass its return value back across threads in a safe, synchronized way.
Please note that the NSRunLoop
interface is not thread safe, but the CFRunLoop
interface is thread safe, so if you need to access another thread's run loop, you should use the CFRunLoop
interface.
Also note that you should probably not run a run loop for very long inside a block running on a GCD queue, because you're tying up a thread that GCD expects to control. If you need to run a run loop for a long time, you should start your own thread for it. You can see an example of this in the _legacyStreamRunLoop function in CFStream.c. Note how it makes the dedicated thread's run loop available in a static variable named sLegacyRL
, which it initializes under the protection of a dispatch_semaphore_t
.