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.sleep为delay,处理时间仍然是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)中调用它。
我只会尝试以一种非常简单的方式回答。
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)Coroutine 2 main首先打印。Coroutine 1 mainCoroutine 2 main
Coroutine 1 main
Run Code Online (Sandbox Code Playgroud)
因此,我们可以证明它将Thread.sleep阻塞线程,而delay只是挂起线程。
挂起线程意味着线程将在必要时“等待”同时执行其他操作。阻塞线程意味着该线程无论如何都会等待不执行任何操作。
您可以使用以下代码证明这一点:
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它是一个挂起函数,因此只能从协程内部或从另一个挂起函数调用。
| 归档时间: |
|
| 查看次数: |
14683 次 |
| 最近记录: |