5

I did the simple test with DispatchQueue:

DispatchQueue.global(qos: .background).sync {
  if Thread.isMainThread {
    print("Main thread")
  }
}

It printed out:

Main thread

Why does this code execute on the main thread? It should be performed on a background thread (it was added to a background queue), right?

Vuong Cuong
  • 192
  • 9
  • 2
    From the documentation of the `sync` function: "As an optimization, this function invokes the block on the current thread when possible." – dan Oct 23 '18 at 16:40
  • 5
    Don’t confuse queues and threads. Work with queues and let them worry about the threads for you! – matt Oct 23 '18 at 16:43

2 Answers2

4

Because it doesn't actually have to. You're blocking the main thread by using sync. iOS is choosing to just execute it on the main thread instead of bothering to switch to a background thread (of a background queue) as it doesn't really matter due to main thread being blocked anyways.

Apple's docs (and quickhelp) on the sync function include the line:

As an optimization, this function invokes the block on the current thread when possible.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Smartcat
  • 2,834
  • 1
  • 13
  • 25
3

The problem is you asked the wrong question. Don't confuse queues and threads. Work with queues and let them rejigger the threads as needed. That is exactly why we have queues! You have no business worrying what thread you're on. All you need to know is that you're on the right queue, which you can find out like this:

let q = DispatchQueue.global(qos: .background)
q.sync {
    dispatchPrecondition(condition: .onQueue(q))
    print("it's okay! we're on the right queue") // yep
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Hi, thanks for your info. I just want to have more understanding about queues and threads! I'm pretty sure that I'm not confused about them but still wonder why this block happened on the main thread. I think "the optimization" is an acceptable answer. – Vuong Cuong Oct 23 '18 at 17:06
  • @Matt Marginally related question, but is there a best-practice way of how to do this? https://pastebin.com/R5KeWrjY – Alexander Oct 23 '18 at 17:33
  • @Alexander That probably _is_ the best practice as far as it goes (https://stackoverflow.com/questions/10330679/how-to-dispatch-on-main-queue-synchronously-without-a-deadlock) but even better practice is to ask yourself whether you really need to call `main.sync` in the first place. – matt Oct 23 '18 at 17:39
  • Yup, if you want work done on a background queue, write your code such that your main queue can kick off the work on that background queue, then continue along and then your main queue code can be notified during next yield cycle in some manner (such as via a completion closure called on main queue) when the work is completed in the background queue. – Smartcat Oct 23 '18 at 17:41
  • @matt In my particular case, it was to call `reloadData()` on a view table views managed by my VC. There are multiple contexts in which reloading the data was necessary. Some were on the main thread (part of `@IBAction`s), while some were off the main thread (asynchronous rx code). – Alexander Oct 23 '18 at 17:50
  • @Alexander What Smartcat said. Your whole architecture was / is wrong. Let's drop this comment chain; if you want to ask about this as a question, please do! – matt Oct 23 '18 at 17:52