Фон
Я нашел некоторую библиотеку анимации GIF , которая имеет фоновый поток, который постоянно декодирует текущий кадр в растровое изображение, являясь производителем для другого потока:
@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, который я сделал для этого, доступен здесь .
Проблема
Это неэффективно, потому что, когда поток достигает точки, которая mIsPlaying
ложна, он просто ждет и постоянно проверяет это. Фактически, это заставляет этот поток как-то больше загружать процессор (я проверял через профилировщик).
Фактически, он увеличивается с 3–5% ЦП до 12–14% ЦП.
Что я пробовал
В прошлом я хорошо знал о потоках, и я знаю, что просто ставить wait
и notify
опасно, так как это может привести к тому, что поток будет ждать в некоторых редких случаях. Например, когда он определил, что он должен ждать, а затем, прежде чем он начнет ждать, внешний поток помечает его как ожидающий.
Это поведение называется «занятое вращение» или «Ожидание при занятости», и на самом деле есть несколько решений об этом, в случае нескольких потоков, которые должны работать вместе, здесь .
Но здесь я думаю, что это немного по-другому. Ожидание не для некоторого потока, чтобы закончить свою работу. Это для временного ожидания.
Другая проблема заключается в том, что потребительский поток является потоком пользовательского интерфейса, поскольку именно он должен получить растровое изображение и просмотреть его, поэтому он не может просто ждать работу, как решение потребительского производителя (пользовательский интерфейс никогда не должен ждать , как это может вызвать "джанк").
Вопрос
Какой правильный способ избежать вращения здесь?