need some clarifications about dispatch queue, thread and NSRunLoop
Asked Answered
O

2

32

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)

Operculum answered 23/6, 2016 at 19:57 Comment(9)
Why do you want/need a runloop on a background thread? If you describe what you're trying to do, we might be able to suggest better patterns.Spud
@Rob, I am not saying I need a runloop on background thread, I am just confused about it, that's why I ask those questions. I am not sure does every thread have a NSRunLoop in iOS/OSX.Operculum
OK. Bottom line, you do not need (nor necessarily want) run loops on every thread. The purpose of a run loop is to keep a thread alive, polling for events. And that is almost antithetical to GCD patterns.Spud
@Rob, still not clear to me. Like you said, the purpose of run loop is to keep thread live & polling for event. OK, then, I have to ask again does thread created by GCD (when dispatching task) have run loop created by iOS/OSX or not? If the answer is no, then how can the thread polling the task dispatched to it? (I am not saying I need that run loop, I just want to know does iOS/OSX create it when dispatch task from queue to the thread.)Operculum
Then I have to answer again that a thread created by GCD does not have a run loop created for it. Your question about "how can the thread poll for tasks dispatched to it" is based upon a false premise, that it's polling. It's not. When you dispatch a block to some queue, GCD goes to its pool of worker threads and runs your block on the appropriate thread. There's no polling, no run loops, etc. The only time you have run loops is for those rare cases where you have some API that requires it.Spud
Thanks @Rob, clear to me now.Operculum
BTW, I might refer you to the Migrating From Threads chapter (and especially the Replacing Run-Loop Code section) in the Concurrency Programming Guide.Spud
@Rob, thanks for the link, I will read it. BTW, I start to think why in main thread, apple prefers to keep using run loop instead of dispatch queue... Do you have any words about this?Operculum
Let us continue this discussion in chat.Spud
S
39
  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.

Sign answered 23/6, 2016 at 20:40 Comment(5)
Thank you for your explanation, I will read the links you provided, and if some questions come, I will comment here.Operculum
Can you elaborate on "Note that GCD will also sometimes use the main thread for a block added to some other queue." Does this relate to UI type things?Horizontal
From the dispatch_sync documentation: “As an optimization, this function invokes the block on the current thread when possible.”Sign
Thanks, one question. Is NSRunLoop used a lot in Swift. I used GCD to put some heavy task in background queue and push back to main thread for UI stuff to avoid screen freeze. Never get a chance to use NSRunLoop in my project. Could you explain a bit in which scenario we should use NSRunLoop and what it could help on?Petrick
The run loop is a big topic. Comments are not a suitable place to explain it.Sign
S
10
  1. The relationship between the main thread’s run loop and the main dispatch queue is merely that they are both run on the main thread and that blocks dispatched to the main queue are interleaved on the main thread with events processed on the main run loop.

    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 synchronization point for an application.

  2. When dispatching to a 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 would create a GCD timer dispatch source.

  3. 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 one is selected from the thread pool when the queue needs it.

  4. If you want to create a NSRunLoop for a worker thread (which you generally should not be doing, anyway), you create it and keep track of it yourself. If you call current, it will create a run loop for you: “If a run loop does not yet exist for the thread, one is created and returned.”

    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 very limited number of worker threads.

Spud answered 23/6, 2016 at 20:22 Comment(2)
About 4. My main question is not about how to make/create a NSRunLoop for worker thread. I want to ask, does each thread (created by GCD when dispatching task) have a NSRunLoop? If so, how can I get this NSRunLoop from dispatch queue?Operculum
Each thread does not have its own run loop, so there is generally nothing to retrieve. There is a method to retrieve the main run loop, but other than that, the only run loops around are those that you manually create yourself.Spud

© 2022 - 2024 — McMap. All rights reserved.