zwl*_*lxt 8 multithreading kotlin kotlin-coroutines
我想了解kotlin协程的执行顺序和线程切换。我曾经withContext
切换到另一个上下文并运行耗时的任务,因此主线程不会被阻塞。但 kotlin 并没有按预期切换上下文。
代码在 kotlin 游乐场上运行:https://pl.kotl.in/V0lbCU25K
不起作用的案例
suspend fun main() = runBlocking {
println("Hello, world!!!")
println(Thread.currentThread().name)
withContext(Dispatchers.IO) {
println("Before heavy load: ${Thread.currentThread().name}")
Thread.sleep(5000)
println("After heavy load: ${Thread.currentThread().name}")
}
println("waiting")
println(Thread.currentThread().name)
}
Run Code Online (Sandbox Code Playgroud)
输出
Hello, world!!!
main @coroutine#1
Before heavy load: DefaultDispatcher-worker-1 @coroutine#1
After heavy load: DefaultDispatcher-worker-1 @coroutine#1
waiting
main @coroutine#1
Run Code Online (Sandbox Code Playgroud)
上面代码块中的函数sleep
与主线程运行在同一线程中并阻塞它。
以下情况符合我的期望(耗时的任务不会阻塞主线程)
情况1
suspend fun main() = runBlocking {
println("Hello, world!!!")
println(Thread.currentThread().name)
launch {
println("Before heavy load: ${Thread.currentThread().name}")
Thread.sleep(5000)
println("After heavy load: ${Thread.currentThread().name}")
}
println("waiting")
println(Thread.currentThread().name)
}
Run Code Online (Sandbox Code Playgroud)
输出
Hello, world!!!
main @coroutine#1
waiting
main @coroutine#1
Before heavy load: main @coroutine#2
After heavy load: main @coroutine#2
Run Code Online (Sandbox Code Playgroud)
案例2
suspend fun main() = runBlocking {
println("Hello, world!!!")
println(Thread.currentThread().name)
launch {
withContext(Dispatchers.IO) {
println("Before heavy load: ${Thread.currentThread().name}")
Thread.sleep(5000)
println("After heavy load: ${Thread.currentThread().name}")
}
}
println("waiting")
println(Thread.currentThread().name)
}
Run Code Online (Sandbox Code Playgroud)
输出
Hello, world!!!
main @coroutine#1
waiting
main @coroutine#1
Before heavy load: DefaultDispatcher-worker-1 @coroutine#2
After heavy load: DefaultDispatcher-worker-1 @coroutine#2
Run Code Online (Sandbox Code Playgroud)
我曾经
withContext
切换到另一个上下文并运行耗时的任务,因此主线程不会被阻塞。但 kotlin 并没有按预期切换上下文。
您的withContext
调用确实释放了主线程。它将工作转移到另一个线程,但此时您的主线程除了等待调用withContext
完成之外无事可做。runBlocking
启动一个事件循环,该循环可以为任意数量的并发协程提供服务,但由于只有一个协程,因此必须完成该协程才能runBlocking
完成该块。
下面演示了线程未被阻塞的含义:
fun main() {
measureTimeMillis {
runBlocking {
launchManyCoroutines()
println("Top-level coroutine sleeping on thread ${currentThread().name}")
delay(2_000)
println("Top-level coroutine done")
}
}.also { println("Program done in $it milliseconds") }
}
private fun CoroutineScope.launchManyCoroutines() {
val cpuCount = getRuntime().availableProcessors()
(1 until cpuCount).forEach { coroId ->
launch { // on the main thread
val sum = withContext(Dispatchers.Default) {
println("Coroutine #$coroId computing on thread ${currentThread().name}")
computeResult()
}
println("Coroutine #$coroId done on thread ${currentThread().name}:" +
" sum = $sum")
}
}
(cpuCount + 1..100).forEach { coroId ->
launch { // on the main thread
println("Coroutine $coroId sleeping 1 s on thread ${currentThread().name}")
delay(1_000)
println("Coroutine #$coroId done on thread ${currentThread().name}")
}
}
}
private fun computeResult(): Int {
val rnd = ThreadLocalRandom.current()
return (1..1_000_000).map { rnd.nextInt() }.sum()
}
Run Code Online (Sandbox Code Playgroud)
该程序启动(100 + 可用处理器)并发协程,全部在主线程上。其中一些用于withContext(Dispatchers.Default)
在线程池上执行 CPU 密集型任务(对一百万个随机整数求和),而另一些则直接在主线程上执行挂起工作(延迟一秒)。最后,顶级协程在完成之前休眠 2 秒。
整个程序在 2 秒多一点的时间内完成,并打印如下内容:
Top-level coroutine sleeping on thread main
Coroutine #2 computing on thread DefaultDispatcher-worker-2
Coroutine #3 computing on thread DefaultDispatcher-worker-3
Coroutine #4 computing on thread DefaultDispatcher-worker-5
Coroutine #1 computing on thread DefaultDispatcher-worker-1
Coroutine #5 computing on thread DefaultDispatcher-worker-6
Coroutine #6 computing on thread DefaultDispatcher-worker-4
Coroutine #7 computing on thread DefaultDispatcher-worker-8
Coroutine 9 sleeping 1 s on thread main
Coroutine 10 sleeping 1 s on thread main
...
Coroutine 99 sleeping 1 s on thread main
Coroutine 100 sleeping 1 s on thread main
Coroutine #3 done on thread main: sum = -1248358970
Coroutine #4 done on thread main: sum = -228252033
Coroutine #6 done on thread main: sum = -147126590
Coroutine #2 done on thread main: sum = -1065374439
Coroutine #1 done on thread main: sum = -2029316381
Coroutine #7 done on thread main: sum = -865387844
Coroutine #5 done on thread main: sum = -1695642504
Coroutine #9 done on thread main
Coroutine #10 done on thread main
...
Coroutine #99 done on thread main
Coroutine #100 done on thread main
Top-level coroutine done
Program done in 2066 milliseconds
Run Code Online (Sandbox Code Playgroud)
请注意,程序执行的所有操作都是在主协程在主线程上休眠时发生的。
长话短说
使用launch
withContext
不做异步。它合并了上下文。要执行异步作业,请使用带有指定协程上下文的启动函数。
withContext
实际上并不运行异步,它合并了上下文。
根据Kotlin 核心文档:
withContext
:
使用给定的协程上下文调用指定的挂起块,挂起直到完成,然后返回结果。
所以这段代码的预期结果使用withContext
:
fun main() = runBlocking {
println("Before block ${Thread.currentThread().name}")
withContext(Dispatchers.IO) {
println("Long op ${Thread.currentThread().name}")
delay(1000)
}
println("After block")
}
Run Code Online (Sandbox Code Playgroud)
是:
Before block main @coroutine#1
Long op DefaultDispatcher-worker-1 @coroutine#1
After block
Run Code Online (Sandbox Code Playgroud)
您可以使用launch
协程内的函数来实现您想要的:
以下代码的结果:
fun main() = runBlocking {
println("Before block ${Thread.currentThread().name}")
launch(Dispatchers.IO) {
println("Long op ${Thread.currentThread().name}")
delay(1000)
}
println("After block")
}
Run Code Online (Sandbox Code Playgroud)
将:
Before block main @coroutine#1
After block
Long op DefaultDispatcher-worker-1 @coroutine#2
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5050 次 |
最近记录: |