В чем разница между задержкой и Thread.sleep в kotlin - PullRequest
1 голос
/ 21 апреля 2020

Я пытаюсь выяснить или доказать, «как задержка приостанавливает функцию».
Итак, я написал здесь пример

var time: Long? = null
    var job1 = GlobalScope.launch() {
        println("Coroutine ${Thread.currentThread().name}")
        time = measureTimeMillis {
            Thread.sleep(1000)
            println("After 1 seconds")
            Thread.sleep(1000)
            println("After 1 seconds")
            Thread.sleep(1000)
            println("After 1 seconds")
        }

    }
    println("Thread ${Thread.currentThread().name}")

    runBlocking {
        job1.join()
        println("Proccesed time is $time")
    }

Вывод, который я получил,

Thread main
Coroutine DefaultDispatcher-worker-1
After 1 seconds
After 1 seconds
After 1 seconds
Proccesed time is 3015

Затем я заменил Thread.sleep на delay, и все же время обработки составляет 3045 ms.

Я не вижу никакой разницы между Thread.sleep с delay.
Как доказать, что это Suspending Function и отличается от Thread.sleep

Ответы [ 3 ]

2 голосов
/ 21 апреля 2020

Разница в том, что delay - это функция приостановки, которая не будет блокировать поток, в то время как Thread.sleep() заблокирует поток.

Другими словами, delay означает, что сопрограмма приостанавливается для это количество времени, которое, в свою очередь, означает, что базовый Thread может бесплатно go обслуживать другую сопрограмму, которая задерживает и приостанавливает и т. д. для всех сопрограмм.

с Thread.sleep с другой стороны, основной поток не может перейти от сопрограммы к сопрограмме - каждый Thread блокируется до тех пор, пока не закончатся все спящие в сопрограмме, прежде чем он сможет go обслуживать выполнение другой сопрограммы.

Для доказательства это для себя с кодом, запустите сотню этих сопрограмм вместо одного. Затем попробуйте это с тысячей. Версия Thread.sleep() будет выполняться дольше и дольше, потому что пул потоков сопрограммного диспетчера по умолчанию быстро израсходован. С другой стороны, версия delay будет выполнена чуть более чем за 3 секунды, потому что нет вызовов, блокирующих потоки сопрограмм.

fun main() {
  var time: Long? = null
  time = measureTimeMillis {
    var jobs = (0..1_000).map {
      GlobalScope.launch {
        delay(1_000)
        print(".")
        delay(1_000)
        print(".")
        delay(1_000)
        print(".")
/*
        Thread.sleep(1_000)
        print(".")
        Thread.sleep(1_000)
        print(".")
        Thread.sleep(1_000)
        print(".")
*/
      }
    }

    runBlocking {
      jobs.joinAll()
    }
  }
  println("")
  println("Processed time is $time")
}

Когда код сопрограмм выполняется так, как должен - без блокировки вызовов, таких как Thread.sleep() - потоки всегда доступны для обслуживания сопрограмм, которые необходимо выполнить. Если для блокирующего вызова нет приостановленного эквивалента (например, сторонней библиотеки, которая выполняет синхронный ввод / вывод), всегда инкапсулируйте эти вызовы в диспетчере, предназначенном для выполнения таких блокирующих вызовов, например withContext(Dispatchers.IO) { ... }, оставляя других сопрограммных диспетчеров бесплатно для обслуживания неблокирующих сопрограмм.

1 голос
/ 22 апреля 2020

Я постараюсь ответить очень простым способом.

fun main() = runBlocking<Unit> {

    launch {
        Thread.sleep(3000L)
     // delay(3000L)
        println("Coroutine 1 ${Thread.currentThread().name}")
    }

    launch {
        println("Coroutine 2 ${Thread.currentThread().name}")
    }
}

Использование Thread.sleep ##

  • Здесь я использовал runBlocking обернуть выполнение основной функции. Что позволяет мне использовать ключевое слово launch напрямую, поскольку оно работает в контексте runBlocking.
  • обе сопрограммы launch будут выполняться в потоке main. Что можно увидеть, напечатав имя потока.
  • Когда Thread.sleep(3000L) помещен внутрь первого запуска. Выходные данные:
Coroutine 1 main
Coroutine 2 main

Это потому, что при выполнении первой сопрограммы она поступит в Thread.sleep (3000L), который, в свою очередь, блокирует основной поток. Таким образом, основной поток блокируется, и он не будет обрабатывать другие потоки в течение 3 секунд.

Затем через 3 секунды будет напечатано Coroutine 1 main. и тогда будет напечатано Coroutine 2 main

Использование задержки

  • теперь замените Thread.sleep(3000L) на delay(3000L)
  • сейчас когда выполняется первая сопрограмма, она приходит с задержкой (3000 л), что приостанавливает основной поток.
  • теперь основной поток не будет ждать 3 секунды, вместо этого он начнет выполнять другие сопрограммы.
  • Следовательно, он выполнит сопрограмму 2 и сначала напечатает Coroutine 2 main.
  • на заднем конце после того, как задержка в 3 секунды будет завершена сопрограммой 1. Затем основная нить go вернется к сопрограмме 1 и напечатает Coroutine 1 main
  • Итак, вывод будет
Coroutine 2 main
Coroutine 1 main

Следовательно, мы можем доказать, что Thread.sleep блокирует поток, тогда как delay просто приостанавливает поток.

1 голос
/ 21 апреля 2020

Приостановка потока означает, что поток тем временем будет «ждать», делая что-то еще, если это необходимо. Блокировка потока означает, что поток будет ждать, ничего не делая.

Вы можете доказать это с помощью этого кода:

fun log(message: String) {
    println("[${Thread.currentThread().name}] : $message")
}

fun main() {
    runBlocking {
        val myThread = newSingleThreadContext("My Thread")

        launch(myThread) {
            (1..3).forEach {
                log("1st launch: $it")
                //delay(1000)
                Thread.sleep(1000)
            }
        }

        launch(myThread) {
            (1..3).forEach {
                log("2nd launch: $it")
                //delay(1000)
                Thread.sleep(1000)
            }
        }
    }
}

Вывод с помощью delay:

[My Thread] : 1st launch: 1
[My Thread] : 2nd launch: 1
[My Thread] : 1st launch: 2
[My Thread] : 2nd launch: 2
[My Thread] : 1st launch: 3
[My Thread] : 2nd launch: 3

Вывод с Thread.sleep:

[My Thread] : 1st launch: 1
[My Thread] : 1st launch: 2
[My Thread] : 1st launch: 3
[My Thread] : 2nd launch: 1
[My Thread] : 2nd launch: 2
[My Thread] : 2nd launch: 3

Поскольку delay является функцией приостановки, ее можно вызывать только из сопрограммы или из другой функции приостановки.

...