简体   繁体   中英

Dispatch to main from a queue that is on main thread

I have a really strange situation where I have multiple nested dispatch queues, and at some point I need to update the UI. DispatchQueue.main.async doesn't execute if I am already on the main thread, if I skip it I can see in the debugger (as well as using Thread.isMainThread ) that I am on the main thread but inside one of the custom queues and the UI doesn't update. How do I get to the “real” main thread ( com.apple.main-thread )? Or how can I ensure that a queue created via

private let progressQueue = DispatchQueue(label: "custom_thread", attributes: .concurrent)

is definitely a background thread?

Your assertion " DispatchQueue.main.async doesn't execute if I am already on the main thread " is not correct. When you dispatch a block of code that block is placed on a queue and executed in order, eg consider this code:

    print ("hello #1 \(Thread.current.description) isMain=\(Thread.isMainThread)")

    DispatchQueue.global().async {
        print ("hello #2 \(Thread.current.description) isMain=\(Thread.isMainThread)")
        
        DispatchQueue.main.async {
            print ("hello #3 \(Thread.current.description) isMain=\(Thread.isMainThread)")
        }
    }

    DispatchQueue.main.async {
        print ("hello #4 \(Thread.current.description) isMain=\(Thread.isMainThread)")
    }
    
    let progressQueue = DispatchQueue(label:"custom_thread", attributes: .concurrent)
    progressQueue.async {
        print ("hello #5 \(Thread.current.description) isMain=\(Thread.isMainThread)")

    }

results in console output:

hello #1 <NSThread: 0x2824c8380>{number = 1, name = main} isMain=true
hello #2 <NSThread: 0x2824f6740>{number = 3, name = (null)} isMain=false
hello #5 <NSThread: 0x2824f6740>{number = 3, name = (null)} isMain=false
hello #4 <NSThread: 0x2824c8380>{number = 1, name = main} isMain=true
hello #3 <NSThread: 0x2824c8380>{number = 1, name = main} isMain=true

Keep in mind that the background queue could execute at any time. The only certainty is that hello #1 will appear first, and that hello #2 will appear before hello #3.

You can also see that progressQueue is definitely a background thread, and appears to be the same as the global dispatch queue.

You said:

DispatchQueue.main.async doesn't execute...

If this dispatched code is not running, then the main queue must be blocked.

I see that Thread.isMainThread is true , but when updating the ui nothin happens

It does not matter that you currently are on the main thread or not, the code dispatched to main via async simply cannot run until the main queue is free again.

How do I get to the “real” main thread ( com.apple.main-thread )? Or how can I ensure that a queue created via... is definitely a background thread?

The dispatch queue that you are creating is a background queue. And GCD decides, at its discretion, on which thread dispatched code will run. Generally, code dispatched to a background queue will run on one of GCD's worker threads of the appropriate QoS, but there are some interesting edge cases.

Specifically, if you dispatch synchronously (ie, with sync ) from the main thread to a GCD queue, as an optimization, GCD may actually run the dispatched code on the main thread. When we sync to a background queue, the main thread is blocked until the dispatched code finishes, anyway, so GCD may just use the idle main thread and avoid a costly context switch. That is why code dispatched synchronously to a background queue may report that isMainThread is still true . But, just to be clear, it is not the case that this is not the “real” main thread. It just is just code dispatched synchronously to some background queue that is actually running on the main thread (and, in your case, likely blocking the main thread in the process).

Bottom line, we do not care what thread this queue happens to be using. The fact that GCD may be using the main thread as an optimization is immaterial. Do not get distracted by isMainThread . We only care whether we have blocked the main thread anywhere.


So, you are going to have to identify where the main thread is getting blocked. Needless to say, we should never block the main thread. By avoiding the blocking of the main thread, we ensure that our UI updates can happen freely. It also provides a better user experience and ensures that the watchdog process will never kill our app.

Given your comments, the problem is likely that sync was called from the main thread. Consistent with the above observation, one simply should not call sync from the main thread. (The one exception is the very fast data synchronizations of memory accessed from multiple threads, but even that is a pattern to be avoided, if you can.)

That having been said, there are other potential blocking patterns that could cause this problem, including (a) semaphores or groups where wait called from the main thread; or (b) some spinning loop.

But in this case, sync is the likely culprit. If you find where you are initially calling sync from the main thread, and change that to adopt an asynchronous pattern, then calls to DispatchQueue.main.async will be free to update the UI.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM