【发布时间】:2018-09-09 14:41:58
【问题描述】:
背景
我发现了一些GIF animation library,它有一个后台线程,不断将当前帧解码为位图,作为其他线程的生产者:
@Volatile
private var mIsPlaying: Boolean = false
...
while (mIsRunning) {
if (mIsPlaying) {
val delay = mGifDecoder.decodeNextFrame()
Thread.sleep(delay.toLong())
i = (i + 1) % frameCount
listener.onGotFrame(bitmap, i, frameCount)
}
}
我为此制作的示例 POC 可用 here。
问题
这是低效的,因为当线程到达mIsPlaying 为假的点时,它只是在那里等待并不断检查它。事实上,它会导致这个线程以某种方式使用更多的 CPU(我通过分析器进行了检查)。
实际上,它从 CPU 的 3-5% 变为 12-14%。
我尝试过的
我过去对线程有很好的了解,并且我知道简单地放置 wait 和 notify 是危险的,因为它仍然可能导致线程在一些罕见的情况下等待。例如,当它确定它应该等待时,然后在它开始等待之前,外部线程将它标记为它不应该等待。
这种行为称为“忙转”或“忙等待”,实际上有一些解决方案,在多个线程需要协同工作的情况下,here。
但在这里我觉得有点不同。等待不是为了某个线程完成它的工作。暂时等待。
这里的另一个问题是消费者线程是 UI 线程,因为它需要获取位图并查看它,所以它不能像消费者-生产者解决方案那样等待工作(UI 绝不能等待,因为它会导致“卡顿”)。
问题
避免在此处旋转的正确方法是什么?
【问题讨论】:
-
wait 和 notify 只能在临界区调用,例如 synchronized(object)。在这里,当您在对象上调用 wait/notify 时,其他线程不会访问这部分代码,因为它没有锁。所以基本上这是避免不必要的 CPU 周期的正确方法。
-
如果您可以定义临时等待,我们或许可以在这里为您提供正确的解决方案。你对 UI 线程也是正确的,但我认为我们需要明确为什么实际上需要等待。
-
@Vipin 我说的是
mIsPlaying为假的情况,所以它一直在做它while(true) {},这意味着它无需等待即可旋转,这需要大量CPU没有什么。我认为它应该有某种信号,使用wait -
@Vipin 在 99% 的情况下
wait/notify是多余的,因为java.util.concurrent包中有很多有用的类,例如CountDownLatch/CyclicBarrier/Phaser等- 但老实说,这一切都可以通过使用HandlerThreadclass 以最小的努力完成 -
我说这段代码在使用
HandlerThread且不使用任何锁的情况下可以简化一半
标签: java android multithreading kotlin busy-waiting