首页 > 解决方案 > 为什么'await'阻塞了kotlin中的线程?

问题描述

'await' 的 api 描述:

在不阻塞线程的情况下等待该值的完成,并在延迟计算完成时恢复,返回结果值或如果延迟被取消则抛出相应的异常。

fun main() = runBlocking {
    val one = async { doSomethingUsefulOne() }
    println("start->" + System.currentTimeMillis() / 1000)
    one.await()
    println("end->" + System.currentTimeMillis() / 1000)
}

suspend fun doSomethingUsefulOne(): Int {
    delay(3000L)
    return 13
}

结果:

开始-> 1575977567

结束->1575977570

'start' 和 'end' 之间有 3 秒的间隔。所以这行 'one.await()' 阻塞了线程。为什么它与 api 所说的不同。

标签: kotlinasync-await

解决方案


不一定会阻塞线程。从您传递给的 lambda 构建的协程的执行runBlocking { ... } 暂停,然后在结果准备好one.await()后恢复。one在此期间,执行协程的线程可以切换到执行另一个协程(如果有的话)。但这不是因为,通过调用runBlocking { ... },您明确指定要在当前单线程中运行协程,独占使用线程直到协程完成,因此当协程挂起并且没有其他协程时阻塞线程跑。

例如,如果您在函数中添加第二对async { ... }+ ,则调用实际上将同时执行。awaittestAsyncAwaitawait

您还可以使用明确支持运行多个协同程序的不同调度程序运行此协同程序,并且您会看到one.await()在这种情况下调用不会阻塞线程。

例如,您可以按如下方式运行:

suspend fun doSomethingUsefulOne(): Int {
    delay(3000L)
    return 13
}

suspend fun testAsyncAwait(n: Int) = coroutineScope {
    val one = async { doSomethingUsefulOne() }
    println("start $n ->" + System.currentTimeMillis() / 1000)
    one.await()
    println("end $n ->" + System.currentTimeMillis() / 1000)
}

suspend fun main() = coroutineScope {
    val context = newSingleThreadContext("MyOwnThread")
    repeat(2) {
        launch(context) { testAsyncAwait(it) }
    }
}

这两个协程将同时运行,并且one.await()不应阻塞单个线程:

start 0 -> 1575982105
start 1 -> 1575982106
end 0 -> 1575982109
end 1 -> 1575982109

请参阅语言参考中的这些部分:


推荐阅读