答案是:协程不知道网络调用或 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
as 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'