简体   繁体   English

从主线程上的队列调度到主线程

[英]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.我有一个非常奇怪的情况,我有多个嵌套的调度队列,并且在某些时候我需要更新 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.如果我已经在主线程上,则DispatchQueue.main.async不会执行,如果我跳过它,我可以在调试器(以及使用Thread.isMainThread )中看到我在主线程上但在自定义线程之一内队列和 UI 不更新。 How do I get to the “real” main thread ( com.apple.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.您的断言“如果我已经在主线程上,则 DispatchQueue.main.async 不会执行”是不正确的。 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:结果控制台 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.唯一可以确定的是 hello #1 将首先出现,并且 hello #2 将出现在 hello #3 之前。

You can also see that progressQueue is definitely a background thread, and appears to be the same as the global dispatch queue.您还可以看到progressQueue绝对是一个后台线程,并且看起来与全局调度队列相同。

You said:你说:

DispatchQueue.main.async doesn't execute... DispatchQueue.main.async不执行...

If this dispatched code is not running, then the main queue must be blocked.如果这个分派的代码没有运行,那么main队列必须被阻塞。

I see that Thread.isMainThread is true , but when updating the ui nothin happens我看到Thread.isMainThreadtrue ,但是在更新 ui 时没有发生

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.不管您当前是否在主线程上,通过async调度到main的代码根本无法运行,直到主队列再次空闲。

How do I get to the “real” main thread ( com.apple.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. GCD 自行决定在哪个线程上运行分派的代码。 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.通常,分派到后台队列的代码将在 GCD 的具有适当 QoS 的工作线程之一上运行,但也有一些有趣的边缘情况。

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.具体来说,如果您从主线程同步(即使用sync )分派到 GCD 队列,作为优化,GCD 可能实际上在主线程上运行分派的代码。 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.当我们sync到后台队列时,主线程被阻塞,直到分派的代码完成,所以 GCD 可能只使用空闲的主线程并避免代价高昂的上下文切换。 That is why code dispatched synchronously to a background queue may report that isMainThread is still true .这就是为什么同步分派到后台队列的代码可能会报告isMainThread仍然为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. GCD 可能使用主线程作为优化的事实并不重要。 Do not get distracted by isMainThread .不要被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.通过避免主线程的阻塞,我们确保我们的 UI 更新可以自由发生。 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.鉴于您的评论,问题很可能是从主线程调用了sync Consistent with the above observation, one simply should not call sync from the main thread.与上述观察一致,根本不应该从主线程调用sync (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.) (一个例外是从多个线程访问的 memory 的数据同步非常快,但如果可以的话,即使这样也是一种模式。)

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;话虽如此,还有其他可能导致此问题的潜在阻塞模式,包括 (a) 从主线程调用wait的信号量或组; or (b) some spinning loop.或(b)一些旋转循环。

But in this case, sync is the likely culprit.但在这种情况下, sync可能是罪魁祸首。 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.如果您发现最初从主线程调用sync的位置,并将其更改为采用异步模式,则对DispatchQueue.main.async的调用将可以自由地更新 UI。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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