need some clarifications about dispatch queue, thread and NSRunLoop

Leem.fin picture Leem.fin · Jun 23, 2016 · Viewed 8.8k times · Source

The following things are what I know & understand:

Global queue is a concurrent queue which can dispatch tasks to multiple threads. The order of executing task is not guaranteed. e.g.:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
 for (int i; i<10; i++) {
  doTask()
 }
})

If I want to dispatch to serial queue, I can use

dispatch_async(dispatch_queue_create("my.serial.queue", nil) {
  ...
}

each time only one task is dispatched to a thread & get executed. The order is FIFO.

===== What I am confused & not fully understand =======

  1. The main thread has a NSRunLoop, looping tasks in main thread. I am wondering what is the relation ship between dispatch queue and run loop? Can I understand it like, if dispatching a task to main thread, the main thread's NSRunLoop get the dispatched task and execute it?

  2. What about global queue which dispatching tasks to multiple threads? Does iOS/OSX system automatically create not only the threads, but also create NSRunLoop for each thread? and then the run loop in each thread get the dispatched task from global queue & execute it?

  3. Who knows the thread? Do dispatch_async() and dispatch_sync() function know to which thread to dispatch task or does the queue knows to which thread to dispatch task?

  4. Is there a way to get NSRunLoop object of the thread(to which the task is dispatched) from dispatch queue programmatically? (this question is related with question 3)

Answer

rob mayoff picture rob mayoff · Jun 23, 2016
  1. 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.

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

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

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