答案是:协程不知道网络调用或 I/O 操作。你必须根据自己的需要编写代码,将繁重的工作封装到不同的协程中,以便它们可以并发执行,因为默认行为是顺序的。
例如:
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here (maybe I/O)
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here (maybe I/O), too
return 29
}
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
println("The answer is ${one + two}")
}
println("Completed in $time ms")
}
会产生这样的结果:
The answer is 42
Completed in 2017 ms
doSomethingUsefulOne() 和 doSomethingUsefulTwo() 将按顺序执行。
如果你想要并发执行,你必须改写:
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
这将产生:
The answer is 42
Completed in 1017 ms
因为 doSomethingUsefulOne() 和 doSomethingUsefulTwo() 将同时执行。
来源:https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#composing-suspending-functions
更新:
关于协程的执行位置,我们可以在 github 项目指南https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#thread-local-data 中阅读:
有时传递一些线程本地数据的能力很方便,但是对于没有绑定到任何特定线程的协程,如果不编写大量样板文件,就很难手动实现。
对于 ThreadLocal 来说,asContextElement 扩展函数就是来救命的。它创建了一个额外的上下文元素,它保留给定 ThreadLocal 的值,并在每次协程切换其上下文时恢复它。
很容易在实际中演示它:
val threadLocal = ThreadLocal<String?>() // declare thread-local variable
fun main(args: Array<String>) = runBlocking<Unit> {
threadLocal.set("main")
println("Pre-main, current thread: ${Thread.currentThread()}, threadlocal value: '${threadLocal.get()}'")
val job = launch(Dispatchers.Default + threadLocal.asContextElement(value = "launch")) {
println("Launch start, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
yield()
println("After yield, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
}
job.join()
println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
}
在这个例子中,我们使用 Dispatchers.Default 在后台线程池中启动新的协程,因此它可以在与线程池不同的线程上工作,但它仍然具有我们使用 threadLocal 指定的线程局部变量的值.asContextElement(value = "launch"),不管协程在哪个线程上执行。因此,输出(带调试)是:
Pre-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'
Launch start, current thread: Thread[CommonPool-worker-1 @coroutine#2,5,main], thread local value: 'launch'
After yield, current thread: Thread[CommonPool-worker-2 @coroutine#2,5,main], thread local value: 'launch'
Post-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'