Повторите задачу в течение срока с задержкой

1

Я должен переместить индикатор в течение определенного периода времени, например, в течение 6 секунд. Я использую сопрограммы и функцию "повторить". Код выполняется за исключением того, что общее время выполнения не соответствует указанному. Ниже мой код.

val progressJob = Job()
var startTime = 0L
CoroutineScope(Dispatchers.Default + progressJob).launch {
    startTime = System.currentTimeMillis()
    repeat(1000) {
        progressBar.progress += 1
        delay(6)
    }
    Log.d(TAG, "total time= ${System.currentTimeMillis() - startTime}")
}

Я ожидаю, что "общее время" будет 6000, но я получаю значения, превышающие 6000, по крайней мере, на 500.

По сути, я просто хочу многократно увеличивать индикатор выполнения в течение определенного периода времени, и я не использую анимацию из-за проблем с производительностью.

Есть ли что-то, что я пропускаю?

Теги:
kotlin
kotlin-coroutines

3 ответа

0

Вы измеряете не только задержку в 6 миллисекунд, но и время, необходимое для выполнения цикла for (скрытое в repeat), плюс время progressBar.progress += 1 и стоимость самой delay.

Например:

CoroutineScope(Dispatchers.Default + progressJob).launch {
    startTime = System.currentTimeMillis()
    repeat(1000){
        delay(6)
    }
    val endTime = System.currentTimeMillis() - startTime
    println("total time= $endTime")
}

на моей машине требуется 6751 мс (в среднем 100 пробежек).

Если я использую Thread.sleep вместо задержки:

CoroutineScope(Dispatchers.Default + progressJob).launch {
    startTime = System.currentTimeMillis()
    repeat(1){
        delay(6)
    }
    val endTime = System.currentTimeMillis() - startTime
    println("total time= $endTime")
}

это занимает 6701мс.

Если я выполню повторить только один раз:

CoroutineScope(Dispatchers.Default + progressJob).launch {
    startTime = System.currentTimeMillis()
    repeat(1){
        Thread.sleep(6)
    }
    val endTime = System.currentTimeMillis() - startTime
    println("total time= $endTime")
}

8ms

Если я удалю повторить:

CoroutineScope(Dispatchers.Default + progressJob).launch {
    startTime = System.currentTimeMillis()         
    Thread.sleep(6)
    val endTime = System.currentTimeMillis() - startTime
    println("total time= $endTime")
}
  • 0
    Спасибо за ваше объяснение. Я не заметил delay внутри цикла, пока вы не укажете на это. Я думаю, это то, откуда приходит дополнительное время
  • 0
    Просто для информации - вы не должны использовать инструменты Thread, когда вы достигаете параллелизма с сопрограммами. Это своего рода побеждает весь смысл.
0

Сопрограмма не дает точных сроков. Если процессор занят выполнением других программ одновременно, сопрограммы могут быть легко задержаны. Используйте класс таймера для точного времени. Вот пример. Я регистрирую время каждую секунду и отменяю таймер через 6 секунд. Результаты отключаются только на несколько миллисекунд.

    var startTime = 0L
    val timer : Timer = Timer()
    val task = object : TimerTask()
    {
        var lastTime = 0L
        override fun run() {
            val now = System.currentTimeMillis()
            if(now/1000 > lastTime/1000 )
            {
                Log.d("timer","total time= ${now - startTime}")
                lastTime = now
            }
            if(now - startTime >= 6000)
            {
                timer.cancel()
            }
    }
    startTime = System.currentTimeMillis()
    timer.scheduleAtFixedRate(task,0,6)
  • 0
    Для некоторых других требований мне придется придерживаться сопрограмм, но спасибо за ваше предложение.
0

так что вы делаете здесь, имитируя прогресс. В идеале, должен быть какой-то способ проверки фактического прогресса вашего бара, его обновления и, когда это будет сделано, окончания. Но если это невозможно, тогда симуляция - ваш выбор.

Итак, с сопрограммами мы имеем дело с многопоточным окружением, и в рамках этого у нас есть наши сопрограммы, которые необходимо продолжать, когда передается контроль над исполнением. В вашей реализации это происходит при delay вызова. По этой причине очень трудно гарантировать, что ваша сопрограмма будет завершена в желаемое время. Все, что может сделать задержка, это сказать, что она не возобновится до того, как "по крайней мере" истечет указанное время, и, вероятно, довольно часто пройдет больше времени, а не точное время.

Итак, как нам заставить это исполниться как можно ближе к желаемому времени? Что нам нужно сделать, это отбросить repeat, и, скорее, проверить, сколько времени прошло, чтобы решить, закончим ли мы. Вот примерная реализация, которая, надеюсь, поможет.

class Bar(val barLength: Int = 1000) {
    var progress = 0
}

suspend fun simulateProgress(bar: Bar, job: Job, totalDurationMillis: Long, incrementsMills: Long): Job {
    var startTime = System.currentTimeMillis()
    return CoroutineScope(Dispatchers.Default + job).launch {
        var totalElapsed = 0L
        while (totalElapsed < totalDurationMillis) {
            totalElapsed = System.currentTimeMillis() - startTime
            val progressRatio = totalElapsed.toDouble()/totalDurationMillis.toDouble()
            bar.progress = (progressRatio * bar.barLength.toDouble()).toInt()
            delay(incrementsMills)
        }
        println("Elapsed: $totalElapsed, Progress: ${bar.progress}")
    }
}

fun main() = runBlocking {
    val job = Job()
    val bar = Bar()
    val progressJob = simulateProgress(bar, job, 6000, 10)
    progressJob.join()
} 
  • 0
    Отбрасывание repeat и замена на while - это ключ. В среднем общее время составляет 601X , что достаточно близко для моего случая использования. Большое спасибо.
  • 0
    @HaytonLeung - нет проблем.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню