Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 为什么';使用上下文';不在';运行阻塞';?_Multithreading_Kotlin_Kotlin Coroutines - Fatal编程技术网

Multithreading 为什么';使用上下文';不在';运行阻塞';?

Multithreading 为什么';使用上下文';不在';运行阻塞';?,multithreading,kotlin,kotlin-coroutines,Multithreading,Kotlin,Kotlin Coroutines,我想了解kotlin协程的执行顺序和线程切换。我使用withContext切换到另一个上下文并运行耗时的任务,这样主线程就不会被阻塞。但科特林并没有像预期的那样切换上下文 代码在kotlin游乐场上运行: 不起作用的案子 suspend fun main() = runBlocking { println("Hello, world!!!") println(Thread.currentThread().name) withContext(Dispatchers.IO)

我想了解kotlin协程的执行顺序和线程切换。我使用
withContext
切换到另一个上下文并运行耗时的任务,这样主线程就不会被阻塞。但科特林并没有像预期的那样切换上下文

代码在kotlin游乐场上运行:

不起作用的案子

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)
}
输出

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
Hello, world!!!
main @coroutine#1
waiting
main @coroutine#1
Before heavy load: main @coroutine#2
After heavy load: main @coroutine#2
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
上述代码块中的
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)
}

输出

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
Hello, world!!!
main @coroutine#1
waiting
main @coroutine#1
Before heavy load: main @coroutine#2
After heavy load: main @coroutine#2
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
案例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)
}
输出

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
Hello, world!!!
main @coroutine#1
waiting
main @coroutine#1
Before heavy load: main @coroutine#2
After heavy load: main @coroutine#2
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
TL;DR

使用
launch

withContext
不执行异步。它合并了上下文。要执行异步作业,请在指定的协同路由上下文中使用启动函数


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")
    }
使用给定的协同路由上下文调用指定的挂起块,挂起直到完成,然后返回结果

因此,使用
with context
,此代码的预期结果是:

fun main() = runBlocking {
        println("Before block ${Thread.currentThread().name}")
        withContext(Dispatchers.IO) {
            println("Long op ${Thread.currentThread().name}")
            delay(1000)
        }
        println("After block")
    }
是:

您可以使用协同程序中的
launch
功能实现所需:

以下代码的结果:

fun main() = runBlocking {
        println("Before block ${Thread.currentThread().name}")
        launch(Dispatchers.IO) {
            println("Long op ${Thread.currentThread().name}")
            delay(1000)
        }
        println("After block")
    }
将是:

Before block DefaultDispatcher-worker-1
After block
IO? DefaultDispatcher-worker-3
我使用
withContext
切换到另一个上下文并运行耗时的任务,这样主线程就不会被阻塞。但科特林并没有像预期的那样切换上下文

您的
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()
}
该程序在主线程上启动(100多个可用处理器)并发协程。其中一些使用
withContext(Dispatchers.Default)
在线程池上执行CPU密集型任务(对一百万个随机整数求和),而另一些则直接在主线程上执行挂起工作(延迟1秒)。最后,顶级协同程序在完成之前会休眠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

请注意,程序所做的一切都是在主协程在主线程上睡眠时发生的。

为什么不使用suspend
sleep()
函数而不是
thread.sleep()
?@Mahdi Malv我尝试了
suspend delay()
函数,它仍然阻止了父线程。您的案例1确实阻止了主线程。它只是碰巧在顶级协同程序中的其余代码运行之后执行。您的案例2可以缩短为
launch(Dispatchers.IO){…}
。对我来说,withContext确实会切换线程(从主线程切换到DefaultDispatch辅助线程)。我认为@MarkoTopolnik的回答在解释withContext如何真正切换线程方面做得更好,但除非您执行启动或异步,否则所有内容仍然是同步的,因此runblocking将只等待withContext块完成。