kotlin中delay和Thread.sleep有什么区别

Vik*_*rya 7 kotlin kotlin-coroutines

我试图弄清楚或证明“延迟如何暂停功能”。
所以,我在这里写了一个例子

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")
    }
Run Code Online (Sandbox Code Playgroud)

我得到的输出是

Thread main
Coroutine DefaultDispatcher-worker-1
After 1 seconds
After 1 seconds
After 1 seconds
Proccesed time is 3015
Run Code Online (Sandbox Code Playgroud)

然后我替换Thread.sleepdelay,处理时间仍然是3045 ms

我没有发现Thread.sleep与 之间有什么区别delay
如何证明它是aSuspending Function且不同于Thread.sleep

Ram*_*man 17

延迟是非阻塞的

主要区别在于,delay挂起函数不会阻塞线程,而 whileThread.sleep()会阻塞线程(当然使用 Loom 时除外,但这是另一个问题)。

换句话说,delay意味着协程挂起该时间量,这又意味着底层Thread可以自由地为另一个延迟和挂起的协程提供服务,对于所有协程来说,依此类推。

另一方面Thread.sleep,底层线程不能从一个协程跳转到另一个协程——而是每个Thread协程都会被阻塞,直到协程中的所有睡眠都结束,然后它才能为另一个协程的执行提供服务。

为了用代码向自己证明这一点,请运行下面的代码,该代码启动 coroutineCount多个协程,每个协程打印“1”,延迟,然后打印“2”,然后再次延迟,然后打印“3”。然后用 做同样的事情sleep

更改coroutineCount为 100,然后更改为 200。该Thread.sleep()版本的执行时间将越来越长,因为只有默认调度程序线程池中可用的线程数(可能是您计算机上的处理器数减一)才能同时运行每个协程。这就是线程饥饿。

另一方面,该delay版本将在 3 秒多一点的时间内执行,因为没有调用阻塞协程线程,并且调度程序中有限数量的线程可以同时为所有协程提供服务。

import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.time.measureTime

suspend fun main() {
  val coroutineCount = 50
  val delayTime = 1_000L

  suspend fun delayDelayDelay() {
    delay(delayTime)
    print("1")
    delay(delayTime)
    print("2")
    delay(delayTime)
    print("3")
  }

  // not a suspend function, sleep is not a suspending call,
  // so suspend is redundant here
  fun sleepSleepSleep() {
    Thread.sleep(delayTime)
    print("1")
    Thread.sleep(delayTime)
    print("2")
    Thread.sleep(delayTime)
    print("3")
  }

  val timeDelay = measureTime {
    coroutineScope {
      // launch `coroutineCount` number of coroutines
      repeat(coroutineCount) {
        launch {
          // The default dispatcher has a limited number of threads (probably
          // number of processors - 1), but the delay version does not block
          // these threads, so all the coroutines will delay simultaneously,
          // and all the coroutines will print "1" simultaneously, so you'll
          // see `coroutineCount` number of "1"s all at once, a wait, and then
          // a bunch of "2"s, and then a bunch of "3"s.
          //
          // This will take just a bit longer than 3 * `delayTime`.
          delayDelayDelay()
        }
      }
    }
  }
  println("")
  println("Took $timeDelay to run $coroutineCount coroutines with delays")

  val timeThreads = measureTime {
    coroutineScope {
      // launch `coroutineCount` number of coroutines
      repeat(coroutineCount) {
        launch {
          // The sleep version uses the same dispatcher and limited number
          // of threads, but since sleep blocks / consumes the thread,
          // only the limited number of threads will run simultaneously.
          // Therefore you'll see a few "1"s, then nothing until those threads
          // are free, then a few "2"s, then nothing until those threads are
          // free, then a few "3"s. Then back to "1"s and so on until all the
          // coroutines are done.
          //
          // This will take much longer than 3 * `delayTime` as long as
          // `coroutineCount` is greater than the number of threads in the
          // default dispatcher. If `coroutineCount` is less than the number
          // of threads in the default dispatcher, it will have the same
          // behavior as the delay version.
          sleepSleepSleep()
        }
      }
    }
  }
  println("")
  println("Took $timeThreads to run $coroutineCount coroutines with sleeps")
}
Run Code Online (Sandbox Code Playgroud)

当协程代码按其应有的方式运行时(不会阻塞调用Thread.sleep()),线程始终可用于服务需要执行的协程。如果没有可用于阻塞调用的挂起等效项(例如,执行同步 I/O 的第三方库),请始终将这些调用封装在设计用于运行此类阻塞调用的调度程序中,例如,withContext(Dispatchers.IO) { ... }让其他协程调度程序可以自由服务非阻塞协程。

如果您创建sleepSleepSleep()一个suspend函数,IntelliJ 实际上会警告您这sleep是一个阻塞调用,可能会导致线程匮乏。

多平台

第二个区别是delay平台调用——换句话说,它可以在 Kotlin 支持的每个平台上使用(本机,包括 iOS、JavaScript、WASM、JVM)。因此,可以在通用代码中进行调用delay

Thread.sleep()调用特定于 JVM,因此只能在面向 JVM 的代码或提供 JVM 兼容性的环境(例如 Android)中调用它。


Vik*_*rya 7

我只会尝试以一种非常简单的方式回答。

fun main() = runBlocking<Unit> {

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

    launch {
        println("Coroutine 2 ${Thread.currentThread().name}")
    }
}
Run Code Online (Sandbox Code Playgroud)

使用 Thread.sleep

  • 这里我用来runBlocking包装main函数的执行。这允许我直接使用launch关键字,因为它在runBlocking.
  • 两个launch协程都将在线程上运行main。通过打印线程名称可以看到。
  • 什么时候Thread.sleep(3000L)放进去第一次发射。输出是
Coroutine 1 main
Coroutine 2 main
Run Code Online (Sandbox Code Playgroud)

这是因为,当第一个协程执行时,它将进入 Thread.sleep(3000L),这又会阻塞主线程。因此,主线程被阻塞,并且在 3 秒内不会处理其他线程。

然后3秒后Coroutine 1 main就会打印出来。然后Coroutine 2 main将被打印

使用延迟

  • 现在替换Thread.sleep(3000L)delay(3000L)
  • 现在,当第一个协程执行时,它将延迟(3000L),从而挂起主线程。
  • 现在主线程不会等待 3 秒,而是开始执行其他协程。
  • 因此,它将执行协程 2 并Coroutine 2 main首先打印。
  • 在协程 1 的 3 秒延迟结束后,在后端。然后主线程返回到协程 1 并打印Coroutine 1 main
  • 所以,输出将是
Coroutine 2 main
Coroutine 1 main
Run Code Online (Sandbox Code Playgroud)

因此,我们可以证明它将Thread.sleep阻塞线程,而delay只是挂起线程。


Gle*_*val 5

挂起线程意味着线程将在必要时“等待”同时执行其他操作。阻塞线程意味着该线程无论如何都会等待不执行任何操作。

您可以使用以下代码证明这一点:

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出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
Run Code Online (Sandbox Code Playgroud)

输出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
Run Code Online (Sandbox Code Playgroud)

由于delay它是一个挂起函数,因此只能从协程内部或从另一个挂起函数调用。