插入同步屏障,但messagequeue没异步消息

依赖同步消息的逻辑会卡住

「插入了同步屏障但队列中没有异步消息」的实际表现:不会“彻底卡住”,而是等待异步消息到来或超时唤醒

源码分析(基于 AOSP)

在 MessageQueue.next() 中:

Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;

            // 同步屏障:target == null
            if (msg != null && msg.target == null) {
                // 遍历找异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());

                if (msg != null) {
                    // 找到了异步消息,取出来返回
                    ...
                    return msg;
                } else {
                    // ❗ 没有异步消息 → 计算下次超时时间,继续 nativePollOnce 阻塞等待
                    nextPollTimeoutMillis = -1; // 无限等待
                    continue;
                }
            }
            ...
        }
    }
}

正确结论如下:

情况 是否“卡死”? 解释
同步屏障 + 有异步消息(如 VSync) 正常工作,执行异步消息
同步屏障 + 没有异步消息 ✅ 可能卡住 会进入 nativePollOnce 无限等待状态,直到异步消息或 wake() 触发唤醒
同步屏障移除前无异步消息插入 ✅ 卡住 UI 不更新,逻辑挂起,但不是线程挂死,可以被 wake 唤醒

更严谨地总结

• 并不是 MessageQueue.next() 不再返回;

• 而是由于找不到异步消息,它 会进入无限等待状态(nextPollTimeoutMillis = -1)

• 除非后续有异步消息加入(比如 VSync 回调),或手动调用 MessageQueue.wake();

• 否则主线程不会主动恢复处理消息,表现为“卡死”。

应该更精确地说:

插入同步屏障后,如果没有异步消息,会导致主线程进入无限等待状态,不会继续处理后续同步消息,表现为依赖同步消息的逻辑卡住。

不影响UI更新

但是UI 还是会更新的,前提是:

异步消息(如 VSync 回调)仍然在运行,Choreographer 的机制没有出问题。

我们回到你最初的条件:

插入了同步屏障,但 MessageQueue 中没有异步消息 —— UI 明明还能更新。

那就必须具体一点:

关键区别:你是在哪一刻插入同步屏障?

如果只是你手动插入同步屏障(比如调试用)

• 而此时 Choreographer 已经注册了 VSync 监听

• 那么下一帧的 VSync 到来后,FrameDisplayEventReceiver 会以异步消息形式进入 MessageQueue,

• ➜ 所以 UI 依然能更新!

换句话说:

即使你插入了同步屏障,只要 还有异步消息能进队列(比如系统自动注入的 VSync),

MessageQueue.next() 依然会返回这些异步消息,执行 doFrame(),UI 照常更新

但真正的问题是——如果异步消息“完全没有”会发生什么?

也就是说:插入屏障,且未来没有任何异步消息(不触发 VSync、不手动插入异步消息)

这时:

• MessageQueue.next() 会在 nativePollOnce(-1) 里阻塞;

• 找不到任何可执行消息;

• ➜ UI 更新不会发生,应用逻辑暂停运行,但不是线程挂死,只是等待

所以你观察到 UI 能更新,是因为系统帮你安排了异步消息(比如 VSync)

你说的**“UI 明明更新了”是对的**,因为系统的 Choreographer 机制会安排异步的 VSync 回调消息,这些能穿过同步屏障,所以:

插入同步屏障 ≠ UI 停止更新

没有异步消息 + 插入同步屏障 ⇒ UI 不再更新(除非 wake)

最终精确总结

情况 是否 UI 更新 原因
插入同步屏障 + 有异步消息(VSync) ✅ 会更新 异步消息穿越屏障被执行,触发 doFrame()
插入同步屏障 + 无异步消息(无 VSync、无 wake) ❌ 不更新 所有同步消息被挡住,异步消息又没来,线程卡在 pollOnce
插入屏障 + 马上手动调用 postAsynchronousMessage() ✅ 会更新 能穿过屏障,唤醒线程

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器